LINQ GroupBy Collection

Is it possible to use GroupBy in LINQ using the collection property?

eg.

void Main() { var t1 = new Test() { Children = new List<string>() { "one", "two" } }; var t2 = new Test() { Children = new List<string>() { "one", "two" } }; var t3 = new Test() { Children = new List<string>() { "one", "three" } }; var tests = new List<Test>() { t1, t2, t3 }; var anon = from t in tests select new { Children = t.Children }; anon.GroupBy(t => t.Children).Dump(); } public class Test { public List<string> Children {get;set;} } 

In this example, I would like to hope for two groups:

Key: List () {"one", "two"} Value: t1, t2

Key: List () {"one", "three"} Value: t3

I understand that anonymous types are not compared by reference, but by matching equality with their public properties.

However, the actual result consists of three groups:

Key: List () {"one", "two"} Value: t1

Key: List () {"one", "two"} Value: t2

Key: List () {"one", "three"} Value: t3

If this is not possible, is there a way to get the result I want?

Hope this clearly explained ...

+7
source share
4 answers

The problem is that the lists are not exactly identical. It compares equality for grouping, and you have two new List<string> s that are not quite equal. However, you can join the strings with a hash code that will produce the correct result:

 tests.GroupBy(t => String.Join(string.Empty, t.Children.Select(c => c.GetHashCode().ToString()))); 
0
source

By default, GroupBy will use referential equality when grouping by lists (which are referential types).

Since you have new instances of the list every time, they are not equal.

However, there is GroupBy overloading that allows you to specify a custom IEqualityComparer so that you can implement your own way of comparing a list of strings, for example.

To implement this, there are many other threads here about comparing two lists.

+4
source

The reason you get 3 groups is because List<T> implements the default equality - the default equality, and not due to the "equality of sequences" of the contained elements between any two lists. If you want such semantics, you will have to implement IEqualityComparer<IList<T>> (or similar) yourself and enter this into the GroupBy query using overload that accepts a comparator equality. Here the sample is executed (for arrays, not for lists, but easily adaptable).

If you are comfortable with the equality set (the order and duplicates are irrelevant), you are in luck: you can directly use the HashSet<T> and the provided CreateSetComparer to implement the comparison:

  var t1 = new Test { Children = new HashSet<string> { "one", "two" } }; var t2 = new Test { Children = new HashSet<string> { "one", "two" } }; var t3 = new Test { Children = new HashSet<string> { "one", "three" } }; var tests = new List<Test> { t1, t2, t3 }; // Only two groups: { one, two } and { one, three } tests.GroupBy(t => t.Children, HashSet<string>.CreateSetComparer()) .Dump(); 
+2
source

I do not think there is a built-in method.

Take a look at Jon Skeet's answer here:

Is there any chance to get unique entries using Linq (C #)?

0
source

All Articles