通过对象中的IDictionary <string,object>属性选择不同的记录

时间:2019-06-28 16:58:09

标签: c# linq

我有以下列表:

public class Foo
{
    public int Qty { get; set; }

    public IDictionary<string, object> Dxo { get; set; }
}

我正在尝试合并具有相同字典值的记录(字典中的所有道具都相同),并对合并记录的数量求和。

我有以下查询:

var result = from f in foos.Select(x => x.Exo).Distinct()
             join p2 in foos on exo equals p2.Exo into gr
             select new
             {
                 Qty = gr.Select(x => x.Qty).Sum(),
                 Exo = exo
             };

查询的不同部分似乎不起作用。我的结果有相同数量的记录。

也许无法以这种方式比较IDictionary。预先感谢!

3 个答案:

答案 0 :(得分:2)

您可以pass自定义IEqualityComparer

from f in foos.Distinct(new FooEqualityComparer())

如下所示:

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
    }
}

答案 1 :(得分:1)

您需要的(以及现成的不可用的)是检查IDictionary<string, object>实例是否相等的功能。

当您使用.Distinct()时,将使用标准.Equals()实现对这些实例进行比较,该实现只是通过引用进行比较,而不是您所需要的。

此外,由于此后您需要处理“相等”的记录,因此最好将.GroupBy().Distinct()一起使用Join,而不是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);
    }
}

现在切换到该比较器。

我从您的疑问中读到了这句话:“字典中的所有道具都是相同的”,因为“每个字典都具有完全相同的键集,并且所有字典都不为空”。那是非常严格的语句,它简化了我们的任务,因此比较器看起来像:

x[k] == y[k]

这里有几点注意事项:

  • 如果字典中的值是相当复杂的对象(即int / string之类的非原始对象),则上方的Distinct需要进一步编码以进行比较,
  • 在这里计算哈希码只是一个伪造的(但这应该可以工作);我永远不会将其用于生产代码

说到这-我将永远不会使用基于LINQ的方法来完成这项任务。我怀疑LINQ到对象的.Net Group By / GetEquals的复杂度在O(n ^ 2)左右(我没有寻找内部实现,但我高度怀疑没有真正的优化可以在此处完成通用方法)。

因此,在GroupBy期间对比较器中每个该死的export class UpdateStepDetailsComponent implements AfterViewInit { @Input() step: LogUpdateStep; @Input() conn: HubConnection; constructor() { } ngAfterViewInit() { this.conn.on("StepProgress", this.hubReturn) } getProgress() { this.conn.invoke("GetProgress", this.step.idupdate, this.step.step) } hubReturn(text) { console.log('return', text) this.step.stepText = text console.log('why is step undefined here?', this.step) } }进行每个字典的枚举和随后的每个项的比较都是一个大麻烦。

我会在foreach循环中进行重做,可能会为散列/等式进行一些缓存,或者可能会更改整个方法以与开始时不同的方式解决该任务,以便从开始就将结果累积在同一对象中。 / p>

答案 2 :(得分:0)

您可以在IDictionary上创建扩展方法IsSameAs。该方法应返回一个布尔值,指示2个字典是否相等。然后,您可以像使用LINQ

foos.Where(f1 => foos.Any(f2 => f1.exo.IsSameAs(f2.exo))).Select(....)

当然,缺点之一是它会与他人相匹配。