GroupBy on complex object(例如List )

使用GroupBy()Count() > 1我试图在列表中查找我的类的重复实例。

这个类看起来像这样:

 public class SampleObject { public string Id; public IEnumerable Events; } 

这就是我实例化和分组列表的方式:

 public class Program { private static void Main(string[] args) { var items = new List() { new SampleObject() { Id = "Id", Events = new List() { "ExampleEvent" } }, new SampleObject() { Id = "Id", Events = new List() { "ExampleEvent" } } }; var duplicates = items.GroupBy(x => new { Token = x.Id, x.Events }) .Where(g => g.Count() > 1) .Select(g => g.Key) .ToList(); } } 

duplicates项不包含任何项目。 如何进行分组工作?

要使对象与许多LINQ的运算符(例如GroupByDistinct ,您必须实现GetHashCodeEquals ,或者必须提供自定义比较器。

在您的情况下,将属性作为列表,您可能需要一个比较器,除非您将列表设为只读。

试试这个比较器:

 public class SampleObjectComparer : IEqualityComparer { public bool Equals(SampleObject x, SampleObject y) { return x.Id == y.Id && x.Events.SequenceEqual(y.Events); } public int GetHashCode(SampleObject x) { return x.Id.GetHashCode() ^ x.Events.Aggregate(0, (a, y) => a ^ y.GetHashCode()); } } 

现在这段代码有效:

  var items = new List() { new SampleObject() { Id = "Id", Events = new List() { "ExampleEvent"} }, new SampleObject() { Id = "Id", Events = new List() { "ExampleEvent" } } }; var comparer = new SampleObjectComparer(); var duplicates = items.GroupBy(x => x, comparer) .Where(g => g.Count() > 1) .Select(g => g.Key) .ToList(); 

List没有重写的Equals + GetHashCode ,这就是你的GroupBy无法按预期工作的原因。 匿名类型的两个属性之一是指列表,当GroupBy必须比较两个列表Object.RefernceEquals ,使用Object.RefernceEquals ,它只检查两者是否是相同的引用,如果两者都包含样本元素则不会。

您可以提供自定义IEqualityComparer

 public class IdEventComparer : IEqualityComparer { public bool Equals(SampleObject x, SampleObject y) { if (object.ReferenceEquals(x, y)) return true; if (x == null || y == null) return false; if(x.Id != y.Id) return false; if (x.Events == null && y.Events == null) return true; if (x.Events == null || y.Events == null) return false; return x.Events.SequenceEqual(y.Events); } public int GetHashCode(SampleObject obj) { if(obj == null) return 23; unchecked { int hash = 23; hash = (hash * 31) + obj.Id == null ? 31 : obj.Id.GetHashCode(); if (obj.Events == null) return hash; foreach (string item in obj.Events) { hash = (hash * 31) + (item == null ? 0 : item.GetHashCode()); } return hash; } } } 

然后你可以在许多LINQ方法中使用它,比如GroupBy

 var duplicates = items.GroupBy(x => x, new IdEventComparer()) .Where(g => g.Count() > 1) .Select(g => g.Key) .ToList(); 

GroupBy()将执行默认比较,使其找到不相等的列表。

请参阅以下代码:

 var eventList1 = new List() { "ExampleEvent" }; var eventList2 = new List() { "ExampleEvent" }; Console.WriteLine(eventList1.GetHashCode()); Console.WriteLine(eventList2.GetHashCode()); Console.WriteLine(eventList1.Equals(eventList2)); 

两个“平等”名单,对吗? 但是,这将打印:

 796641852 1064243573 False 

所以他们不被认为是平等的,因此没有分组。

您需要提供自定义比较器,以比较对象的相关属性。 请注意,如前所示, List.GetHashCode()无法正确表示列表中的项目。

你可以这样做(来自Good GetHashCode()覆盖关于订单的List of Foo对象和多个ref-type字段上的LINQ GroupBy; Custom EqualityComparer ):

 public class SampleObjectComparer : IEqualityComparer { public bool Equals(SampleObject a, SampleObject b) { return a.Id == b.Id && a.Events.SequenceEqual(b.Events); } public int GetHashCode(SampleObject a) { int hash = 17; hash = hash * 23 + a.Id.GetHashCode(); foreach (var evt in a.Events) { hash = hash * 31 + evt.GetHashCode(); } return hash; } } 

并像这样使用它:

 var eventList1 = new List() { "ExampleEvent" }; var eventList2 = new List() { "ExampleEvent" }; var items = new List() { new SampleObject() { Id = "Id", Events = eventList1 }, new SampleObject() { Id = "Id", Events = eventList2 } }; var duplicates = items.GroupBy(x => x, new SampleObjectComparer()) .Where(g => g.Count() > 1) .Select(g => g.Key) .ToList(); Console.WriteLine(duplicates.Count);