45298


Linq - group by using the elements inside an array property  
<https://stackoverflow.com/a/31724862/7882280>

int配列をキーとするようなものは見つかりましたが、オブジェクトの配列でGroupByをするようなものは見つかりませんでした。  
上記を参考に実装してみたものが以下になります。  
(Utf8Jsonを使用しています。)

[code]

    public class AAA
    {
        public int Key { get; set; }
        public string Value { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var group = GetValues().GroupBy(x => x, new ArrayComparer<AAA>()).ToArray();
    
            // Count:200000 Json:[{ "Key":1,"Value":"a"},{ "Key":2,"Value":"bb"}]
            // Count:100000 Json:[{ "Key":1,"Value":"a"},{ "Key":2,"Value":"ccc"}]
            // Count:100000 Json:[{ "Key":1,"Value":"a"},{ "Key":2,"Value":"bb"},{ "Key":3,"Value":"ccc"}]
            foreach (var g in group)
            {
                Console.WriteLine($"Count:{g.Count()} Json:{JsonSerializer.ToJsonString(g.Key)}");
            }
        }
    
        public static IEnumerable<AAA[]> GetValues()
        {
            for (int i = 0; i < 100000; i++)
            {
                yield return new AAA[]
                {
                    new AAA { Key = 1, Value = "a" },
                    new AAA { Key = 2, Value = "bb" },
                };
                yield return new AAA[]
                {
                    new AAA { Key = 1, Value = "a" },
                    new AAA { Key = 2, Value = "bb" },
                };
                yield return new AAA[]
                {
                    new AAA { Key = 1, Value = "a" },
                    new AAA { Key = 2, Value = "ccc" },
                };
                yield return new AAA[]
                {
                    new AAA { Key = 1, Value = "a" },
                    new AAA { Key = 2, Value = "bb" },
                    new AAA { Key = 3, Value = "ccc" },
                };
            }
        }
    }
    
    class ArrayComparer<T> : IEqualityComparer<IList<T>>
    {
        private Dictionary<string, int> _hashes = new Dictionary<string, int>();
    
        public bool Equals(IList<T> x, IList<T> y)
        {
            return true;
        }
    
        public int GetHashCode(IList<T> obj)
        {
            var json = JsonSerializer.ToJsonString(obj);
    
            if (!_hashes.ContainsKey(json))
            {
                var hash = json.GetHashCode();
    
                if (!_hashes.ContainsValue(hash))
                {
                    do
                    {
                        hash++;
                    } while (_hashes.ContainsValue(hash));
                }
    
                _hashes.Add(json, hash);
            }
    
            return _hashes[json];
        }
    }
    
[/code]

このコードは正しいですか？  
また、もっと早くする方法はありますか？


CC BY-SA 4.0
2018-07-04T04:13:11.167
45297
2018-07-04T04:40:26.550
21330
question
c#
T[]の値をキーとしたGroupBy


>
[code]

>     public bool Equals(IList<T> x, IList<T> y)
>     {
>         return true;
>     }
>  
[/code]

このコードはあり得ません。[Object.GetHashCode() 継承時の注意](https://msdn.microsoft.com/ja-
jp/library/system.object.gethashcode\(v=vs.110\).aspx#Inheritors)や[クラスライブラリ開発のデザインガイドライン
Equalsメソッドの実装](https://msdn.microsoft.com/ja-
jp/library/336aedhh\(v=vs.100\).aspx)を確認してください。

.NET 4で[StructuralComparisons](https://msdn.microsoft.com/ja-
jp/library/system.collections.structuralcomparisons\(v=vs.110\).aspx)が追加されています。これを使うと要素の中身に応じた比較が簡単に記述できます。ただし、提供されるのは`IEqualityComparer`であって`IEqualityComparer<T>`ではないため`GroupBy`で要求されるインターフェースを実装する[クラスを用意](https://sayurin.blogspot.com/2011/07/istructuralequatable.html)する必要がある点と、`AAA`クラスの`Equals`が定義されていないためこのままではやはり比較できません。クラスでなく構造体にすると、全ての要素で比較されます。

[code]

    public struct AAA
    {
        public int Key { get; set; }
        public string Value { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var group = GetValues().GroupBy(x => x, new StructuralEqualityComparer<AAA[]>()).ToArray();
    
            // Count:200000 Json:[{ "Key":1,"Value":"a"},{ "Key":2,"Value":"bb"}]
            // Count:100000 Json:[{ "Key":1,"Value":"a"},{ "Key":2,"Value":"ccc"}]
            // Count:100000 Json:[{ "Key":1,"Value":"a"},{ "Key":2,"Value":"bb"},{ "Key":3,"Value":"ccc"}]
            foreach (var g in group)
            {
                Console.WriteLine($"Count:{g.Count()} Json:{JsonSerializer.ToJsonString(g.Key)}");
            }
        }
    
        public static IEnumerable<AAA[]> GetValues()
        {
            for (int i = 0; i < 100000; i++)
            {
                yield return new AAA[]
                {
                    new AAA { Key = 1, Value = "a" },
                    new AAA { Key = 2, Value = "bb" },
                };
                yield return new AAA[]
                {
                    new AAA { Key = 1, Value = "a" },
                    new AAA { Key = 2, Value = "bb" },
                };
                yield return new AAA[]
                {
                    new AAA { Key = 1, Value = "a" },
                    new AAA { Key = 2, Value = "ccc" },
                };
                yield return new AAA[]
                {
                    new AAA { Key = 1, Value = "a" },
                    new AAA { Key = 2, Value = "bb" },
                    new AAA { Key = 3, Value = "ccc" },
                };
            }
        }
    }
    
    public class StructuralEqualityComparer<T> : IEqualityComparer<T> where T: IStructuralEquatable {
        public bool Equals(T x, T y) {
            return StructuralComparisons.StructuralEqualityComparer.Equals(x, y);
        }
        public int GetHashCode(T obj) {
            return StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
        }
    }
    
[/code]


CC BY-SA 4.0
2018-07-04T04:40:26.550
45298
2018-07-04T04:40:26.550
4236
45297
answer