C# 按IDictionary选择不同的记录<;字符串,对象>;对象中的属性
我有以下清单:C# 按IDictionary选择不同的记录<;字符串,对象>;对象中的属性,c#,linq,C#,Linq,我有以下清单: public class Foo { public int Qty { get; set; } public IDictionary<string, object> Dxo { get; set; } } 我的查询的独特部分似乎不起作用。我的结果有相同数量的记录 也许以这种方式比较IDictionary是不可能的。提前谢谢 您可以在IDictionary上创建扩展方法IsSameAs。该方法应返回一个布尔值,指示两个字典是否相等。然后你就可以像这样
public class Foo
{
public int Qty { get; set; }
public IDictionary<string, object> Dxo { get; set; }
}
我的查询的独特部分似乎不起作用。我的结果有相同数量的记录
也许以这种方式比较IDictionary是不可能的。提前谢谢 您可以在IDictionary上创建扩展方法
IsSameAs
。该方法应返回一个布尔值,指示两个字典是否相等。然后你就可以像这样使用LINQ了
foos.Where(f1 => foos.Any(f2 => f1.exo.IsSameAs(f2.exo))).Select(....)
当然,一个缺点是它会与自我和他人匹配。您可以自定义:
看起来是这样的:
class FooEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo one, Foo two)
{
// your implementation goes here
// where you for instance compare Exo
}
public int GetHashCode (Foo foo)
{
// your implementation goes here
}
}
class FooEqualityComparer:IEqualityComparer
{
公共布尔等于(Foo-one,Foo-two)
{
//你的实现就在这里
//例如,您可以比较Exo
}
公共int GetHashCode(Foo-Foo)
{
//你的实现就在这里
}
}
您需要的(以及开箱即用不可用的)是检查IDictionary
实例是否相等的功能
当您使用.Distinct()
时,将使用标准的.Equals()
实现来比较这些实例,这只是通过引用进行比较,而不是您需要的
此外,由于您需要在此之后处理“相等”记录,因此最好使用.GroupBy()
而不是.Distinct()
和连接,因为这更明显,性能也更高(无论如何,我们都可以在此任务中讨论性能)
因此,假设我们有一些可以比较项目的FooComparer
,您的查询应该如下所示:
var result = foos
.GroupBy(x => x.Dxo, x => x, new FooComparer())
.Select(x => new Foo
{
Qty = x.Sum(y => y.Qty),
Dxo = x.First().Dxo,
})
.ToArray();
public class FooComparer : IEqualityComparer<IDictionary<string, object>>
{
public bool Equals(IDictionary<string, object> x, IDictionary<string, object> y)
{
return x.Keys.All(k => x[k] == y[k]);
}
public int GetHashCode(IDictionary<string, object> obj)
{
return obj.Aggregate(0, (hash, x) => (x.Value?.GetHashCode() ?? 0) ^ hash);
}
}
现在切换到比较器
我从你的问题“字典中的所有道具都是一样的”中读到了这句话,就像“每本字典都有完全相同的密钥集,并且所有字典都不是空的”。这是一条非常严格的语句,它简化了我们的任务,因此comparer可以如下所示:
var result = foos
.GroupBy(x => x.Dxo, x => x, new FooComparer())
.Select(x => new Foo
{
Qty = x.Sum(y => y.Qty),
Dxo = x.First().Dxo,
})
.ToArray();
public class FooComparer : IEqualityComparer<IDictionary<string, object>>
{
public bool Equals(IDictionary<string, object> x, IDictionary<string, object> y)
{
return x.Keys.All(k => x[k] == y[k]);
}
public int GetHashCode(IDictionary<string, object> obj)
{
return obj.Aggregate(0, (hash, x) => (x.Value?.GetHashCode() ?? 0) ^ hash);
}
}
公共类FooComparer:IEqualityComparer
{
公共布尔等于(IDictionary x,IDictionary y)
{
返回x.Keys.All(k=>x[k]==y[k]);
}
public int GetHashCode(IDictionary obj)
{
返回obj.Aggregate(0,(散列,x)=>(x.Value?.GetHashCode()?0)^hash);
}
}
这里有几条通知:
- 如果字典中的值是非常复杂的对象(即int/string之类的非原始对象),那么上面的
x[k]==y[k]
需要进一步编码来比较它们
- 在这里计算hashcode只是一个伪代码(但这应该是可行的);我决不会将其用于生产代码
说到这里,我永远不会使用基于LINQ的方法来完成这样的任务。我怀疑.NetDistinct
/Group By
的复杂性对于LINQ to对象来说大约是O(n^2)(我还没有寻找内部实现,但我高度怀疑对于通用方法来说并没有真正的优化)
因此,在GroupBy期间,在comparer中为每个该死的GetEquals
,枚举每一个字典,并对其中的每一项进行后续比较,这是一个巨大的失败
我会在foreach循环中对其进行修改,可能会对hash/equals进行一些缓存,或者可能会改变整个方法,从一开始就以不同的方式解决该任务,以便从一开始就在同一对象中累积结果。您没有任何方法将一个字典与另一个字典进行比较,并且字典类中也没有标准的比较方法。on exo等于p2。exo
← 那不行。联接可以比较基元类型(如字符串、int等),也可以使用匿名类型,其中属性是具有相同属性名称的基元类型(示例on new{Name=“dave”}等于new{Name=o.Name}
。Dictionary是一种复杂的类型,因此如果您想在Dictionary上执行联接,您需要编写一些比较运算符或实现一个可以执行此操作的接口,但我不认为您可以在联接中执行此操作。我建议您使用一些foreach
循环写出您试图执行的操作,并将其实际用于ork(不是伪代码)。那么你将有更好的机会将其转换为linq。你可以创建一个完整的,并将其添加到你的问题中。非常感谢你的非常详细的回答。我将把它投入到生产中。顺便说一句,由于我正在尝试查找类似IDictionary的对象(而且很匆忙),我只是“扁平的"将整个对象转换为json字符串,然后比较字符串。现在完成了这项工作。再次感谢!!感谢您的回复。我非常感激。所有的答案几乎都建议使用相同的方法,但我将其授予@Lanorkin,因为他回复的详细程度。@juliandromon:他发布的答案比我晚,所以我没有看到它。公平地说,他值得接受。谢谢!谢谢你的回复。我非常感谢。所有的答案几乎都表明了相同的方法,但我把它授予@Lanorkin,因为他回复中的详细程度。