C# 复杂对象上的分组依据(例如列表<;T>;)
使用C# 复杂对象上的分组依据(例如列表<;T>;),c#,linq,comparison,grouping,C#,Linq,Comparison,Grouping,使用GroupBy()和Count()>1我试图在列表中找到我的类的重复实例 该类如下所示: public class SampleObject { public string Id; public IEnumerable<string> Events; } var eventList1 = new List<string>() { "ExampleEvent" }; var eventList2 = new List<string>() {
GroupBy()
和Count()>1
我试图在列表中找到我的类的重复实例
该类如下所示:
public class SampleObject
{
public string Id;
public IEnumerable<string> Events;
}
var eventList1 = new List<string>() { "ExampleEvent" };
var eventList2 = new List<string>() { "ExampleEvent" };
var items = new List<SampleObject>()
{
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);
公共类SampleObject
{
公共字符串Id;
公共事件;
}
这就是我实例化和分组列表的方式:
public class Program
{
private static void Main(string[] args)
{
var items = new List<SampleObject>()
{
new SampleObject() { Id = "Id", Events = new List<string>() { "ExampleEvent" } },
new SampleObject() { Id = "Id", Events = new List<string>() { "ExampleEvent" } }
};
var duplicates = items.GroupBy(x => new { Token = x.Id, x.Events })
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
}
}
公共类程序
{
私有静态void Main(字符串[]args)
{
var items=新列表()
{
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)
.选择(g=>g.Key)
.ToList();
}
}
重复的
不包含任何项目。如何使分组工作?要使对象与许多LINQ运算符(如GroupBy
或Distinct
)一起工作,必须实现GetHashCode
&Equals
,或者必须提供自定义比较器
在您的情况下,如果将属性作为列表,您可能需要一个比较器,除非您将该列表设置为只读
试试这个比较器:
public class SampleObjectComparer : IEqualityComparer<SampleObject>
{
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());
}
}
公共类SampleObjectComparer:IEqualityComparer
{
公共布尔等于(采样对象x、采样对象y)
{
返回x.Id==y.Id&&x.Events.SequenceEqual(y.Events);
}
公共int GetHashCode(SampleObject x)
{
返回x.Id.GetHashCode()^x.Events.Aggregate(0,(a,y)=>a^y.GetHashCode());
}
}
现在,该代码起作用了:
var items = new List<SampleObject>()
{
new SampleObject() { Id = "Id", Events = new List<string>() { "ExampleEvent"} },
new SampleObject() { Id = "Id", Events = new List<string>() { "ExampleEvent" } }
};
var comparer = new SampleObjectComparer();
var duplicates = items.GroupBy(x => x, comparer)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
var items=新列表()
{
new SampleObject(){Id=“Id”,Events=new List(){“ExampleEvent”},
new SampleObject(){Id=“Id”,Events=new List(){“ExampleEvent”}
};
var comparer=新的SampleObjectComparer();
var duplicates=items.GroupBy(x=>x,比较器)
.Where(g=>g.Count()>1)
.选择(g=>g.Key)
.ToList();
列表
没有被覆盖的等于
+GetHashCode
,这就是为什么您的分组依据
不能按预期工作的原因。当GroupBy
必须比较两个列表对象时,匿名类型的两个属性之一引用列表。使用referenceequals
仅检查两者是否为相同的引用,而不检查两者是否都包含示例元素
您可以提供一个自定义的IEqualityComparer
:
GroupBy()
将执行默认比较,导致它发现您的列表不相等
请参阅以下代码:
var eventList1 = new List<string>() { "ExampleEvent" };
var eventList2 = new List<string>() { "ExampleEvent" };
Console.WriteLine(eventList1.GetHashCode());
Console.WriteLine(eventList2.GetHashCode());
Console.WriteLine(eventList1.Equals(eventList2));
因此,它们被认为是不平等的,因此没有分组
您需要提供一个自定义比较器,用于比较对象的相关属性。请注意,如前所示,List.GetHashCode()
不能正确表示列表中的项目
您可以这样做(从和):
公共类SampleObjectComparer:IEqualityComparer
{
公共布尔等于(采样对象a、采样对象b)
{
返回a.Id==b.Id
&&a.事件顺序相等(b.事件);
}
公共int GetHashCode(SampleObject a)
{
int hash=17;
hash=hash*23+a.Id.GetHashCode();
foreach(a.Events中的var evt)
{
hash=hash*31+evt.GetHashCode();
}
返回散列;
}
}
然后像这样使用它:
public class SampleObject
{
public string Id;
public IEnumerable<string> Events;
}
var eventList1 = new List<string>() { "ExampleEvent" };
var eventList2 = new List<string>() { "ExampleEvent" };
var items = new List<SampleObject>()
{
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);
var eventList1=new List(){“examplevent”};
var eventList2=新列表(){“ExampleEvent”};
var items=新列表()
{
新建SampleObject(){Id=“Id”,Events=eventList1},
新建SampleObject(){Id=“Id”,Events=eventList2}
};
var duplicates=items.GroupBy(x=>x,新的SampleObjectComparer())
.Where(g=>g.Count()>1)
.选择(g=>g.Key)
.ToList();
Console.WriteLine(副本数);
默认情况下,列表不会根据其项的值进行比较。@SergeyBerezovskiy-这不是问题所在。问题是缺少GetHashCode
和Equals
覆盖。@Enigmativitynew{Token=x.Id,x.Events}
与覆盖SampleObject
的Equals和GetHashCode无关。这里的问题完全是由x.Events
comparison引起的虽然我不会使用,
作为分隔符,@Farhad(string.Join
)提出的解决方案是经典的,而且通常“足够好”。我将使用'\0'
作为分隔符。另一个“东西”。。。然后我将使用.Select(g=>g.First())
检索重复的项目。@SergeyBerezovskiy-是的,很公平。我不得不求助于使用自定义比较器与原始对象进行比较。嗨,蒂姆。非常好的例子和解释。但是,当我尝试实现此功能时,使用以下行:var groupedOrderList=orders.OrderListD.GroupBy(u=>u.Services[0].FeatureList,new FeatureAttributeComparer()).Select(grp=>grp.First()).ToList();我在GroupBy子句下得到了一个标记为:无法从用法推断类型参数的错误。尝试显式指定类型参数。你知道这个错误是什么吗?
public class SampleObjectComparer : IEqualityComparer<SampleObject>
{
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<string>() { "ExampleEvent" };
var eventList2 = new List<string>() { "ExampleEvent" };
var items = new List<SampleObject>()
{
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);