是否可以使用集合属性在LINQ中进行GroupBy?
e.g。
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;}
}
在这个例子中,我希望有两组:
键:List(){“one”,“two”}值:t1,t2
键:List(){“one”,“three”}值:t3
我的理解是,匿名类型不是通过引用进行比较,而是通过比较公共属性的相等性来进行比较。
但实际结果是三组:
键:List(){“one”,“two”}值:t1
键:List(){“one”,“two”}值:t2
键:List(){“one”,“three”}值:t3
如果无法做到这一点,有没有办法得到我想要的结果?
希望能清楚地解释清楚......
答案 0 :(得分:4)
默认情况下,GroupBy
在按列表分组(引用类型)时将使用引用相等。
由于每次都有新的列表实例,因此它们并不相同。
但是,GroupBy
{{1}}允许您指定自定义overload,以便您可以实现自己的比较字符串列表的方式,例如。< / p>
为了实现这一点,这里有很多IEqualityComparer来比较两个列表。
答案 1 :(得分:2)
获得3个组的原因是因为List<T>
实现了与默认引用相等的相等性,而不是考虑任何两个列表之间所包含元素的“序列相等性”。如果你想要这样的语义,你必须自己实现IEqualityComparer<IList<T>>
(或类似的)并使用接受相等比较器的重载将其注入GroupBy
查询。这是一个sample实现(对于数组,不是列表,但很容易适应)。
如果您对 set 相等感到满意(订单和重复项无关紧要),那么您很幸运:您可以直接使用HashSet<T>
和提供的CreateSetComparer
方法比较器实现:
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 :(得分:0)
问题是列表并不完全相同。它正在比较分组的相等性,并且你有两个新的List<string>
,它们并不完全相同。但是,您可以通过哈希代码加入字符串,这将产生正确的结果:
tests.GroupBy(t => String.Join(string.Empty, t.Children.Select(c => c.GetHashCode().ToString())));
答案 3 :(得分:0)