【问题标题】:Filtering duplicates from a type collection using LINQ使用 LINQ 从类型集合中过滤重复项
【发布时间】:2016-02-12 00:16:51
【问题描述】:

我通过对两个参数进行分组并根据 createdate(使用 first())选择子组列表中的最新类型来过滤列表。 这消除了 x.application 和 x.externalid 属性上的重复项。

var list = ((List<SomeType>)xDic)
            .GroupBy(x => new {x.Application, x.ExternalID})
            .OrderByDescending(z => z.First().CreateDate)
            .Select(y => y.First()).ToList();

我遇到的麻烦是定义另一个属性组合(x.application 和 x.externaldisplayid)来过滤和分组以获取第一个。

总而言之,我需要通过根据 ((x.application/x.externalid) OR (x.application/x.externaldisplayid)) 组合过滤掉任何重复项来获得 SomeTypes 的唯一列表。

Example set:
{ "extID": 1234, "extDspID" : 111, "App" : "Test", "CreateDate": 2/01/2015}
{ "extID": 1234, "extDspID" : 5, "App" : "Test", "CreateDate": 1/01/2015}
{ "extID": 012, "extDspID" : 90, "App" : "Mono", "CreateDate": 6/06/2015}
{ "extID": 999, "extDspID" : 78, "App" : "Epic", "CreateDate": 8/08/2015}
{ "extID": 333, "extDspID" : 78, "App" : "Epic", "CreateDate": 8/12/2015}
{ "extID": 345, "extDspID" : 33, "App" : "Test", "CreateDate": 2/01/2015}
{ "extID": 666, "extDspID" : 33, "App" : "Test", "CreateDate": 1/01/2015}

desired result:
{ "extID": 1234, "extDspID" : 111, "App" : "Test", "CreateDate": 2/01/2015}
{ "extID": 012, "extDspID" : 90, "App" : "Mono", "CreateDate": 6/06/2015}
{ "extID": 333, "extDspID" : 78, "App" : "Epic", "CreateDate": 8/12/2015}
{ "extID": 345, "extDspID" : 33, "App" : "Test", "CreateDate": 2/01/2015}

【问题讨论】:

  • 我正在尝试理解您的问题。你能提供你的类的定义(SomeType)吗?
  • 看起来像 stackoverflow.com/questions/11811110/… 的副本(但不完全确定)
  • @B.ClayShannon - 我必须考虑最近创建的维度,因此 distinct 并不能完全解决我的问题。
  • 你能具体解释一下((x.application/x.externalid) OR (x.application/x.externaldisplayid)) combinations 是什么意思吗?
  • 我怀疑你无法解释你想要的事实是问题所在:) 这些重复项中的所有 3 个都是 {A1,id1,disp1}, {A1,id1,disp2}, {A1 ,id2,disp2}?

标签: c# .net linq duplicates filtering


【解决方案1】:

首先,声明两个相等比较来指定两个条件,如下所示:

public class MyEqualityComparer1 : IEqualityComparer<SomeType>
{
    public bool Equals(SomeType x, SomeType y)
    {
        return x.Application == y.Application && x.ExternalID == y.ExternalID;
    }

    public int GetHashCode(SomeType obj)
    {
        return (obj.Application + obj.ExternalID).GetHashCode();
    }
}

public class MyEqualityComparer2 : IEqualityComparer<SomeType>
{
    public bool Equals(SomeType x, SomeType y)
    {
        return x.Application == y.Application && x.ExternalDisplayId == y.ExternalDisplayId;
    }

    public int GetHashCode(SomeType obj)
    {
        return (obj.Application + obj.ExternalDisplayId).GetHashCode();
    }
}

然后,按CreatedDate 排序您的列表,然后使用Distinct 过滤您的列表,如下所示:

var result = xDic
    .OrderByDescending(x => x.CreateDate)
    .Distinct(new MyEqualityComparer1())
    .Distinct(new MyEqualityComparer2());

Distinct 方法should remove the later items,因此我们应该能够依赖于我们使用OrderByDescending 的事实来确保Distinct 将删除最近的CreatedTime 的项目。

但是,由于 Distinct 的文档不保证这一点,您可以使用自定义的 distinct 方法,如下所示:

public static class Extensions
{
    public static IEnumerable<T> OrderedDistinct<T>(this IEnumerable<T> enumerable, IEqualityComparer<T> comparer)
    {
        HashSet<T> hash_set = new HashSet<T>(comparer);

        foreach(var item in enumerable)
            if (hash_set.Add(item))
                yield return item;
    }
}

并像这样使用它:

var result = xDic
    .OrderByDescending(x => x.CreateDate)
    .OrderedDistinct(new MyEqualityComparer1())
    .OrderedDistinct(new MyEqualityComparer2());

【讨论】:

  • 感谢@Yacoub 的回复。有机会我会尽快尝试的。
  • 有趣 - Distinct(x =&gt; {x.p1}).Distinct(x=&gt;{x.p2}) 给出的结果是否与 Distinct(x =&gt; {x.p2}).Distinct(x=&gt;{x.p1}) 相同(对于 OP 正在寻找的内容应该是正确的)。
  • @AlexeiLevenkov,是的。删除所有重复项,这可以通过调用两个 Distinct 操作来保证(无论调用顺序如何)。唯一剩下的就是确保我们总是删除最近的项目。由于我们对列表进行了排序,因此我们确信被任何 Distinct 操作删除的任何项目在序列的前面都有一个类似的项目,并且具有更新的 CreatedDate
【解决方案2】:

当前接受的答案不会正确排序您的“SomeType”对象,因此不会产生您想要的结果集。

我在这里实现了一个解决方案:

https://dotnetfiddle.net/qBkIXo

我的解决方案也基于 Distinct(请参阅 MSDN 文档here)。我生成哈希的方式是基于this整洁的方法,它使用匿名类型,例如

public int GetHashCode(SomeType sometype)
{
 //Calculate the hash code for the SomeType.
 return new { sometype.Application, sometype.ExternalID }.GetHashCode();
}

为了达到正确的预期结果,需要应用分组、排序和使用不同的组合,例如

    var noduplicates = products.GroupBy(x => new {x.Application, x.ExternalDisplayId})
        .Select(y => y.OrderByDescending(x => x.CreateDate).First())
        .ToList()
        .Distinct(new ApplicationExternalDisplayIdComparer())
        .GroupBy(x => new {x.Application, x.ExternalID})
        .Select(y => y.OrderByDescending(x => x.CreateDate).First())
        .ToList()
        .Distinct(new ApplicationExternalIDComparer());

正如您将在小提琴输出中看到的那样,这给出了您期望的结果。

【讨论】:

  • 为什么您认为我的回答没有正确排序对象?你能解释一下吗?
  • 嗨,Yacoub。我以与您相同的方式实现了该解决方案,但我发现在执行不同的语句之前简单地排序日期不会给出 OP 的预期结果。尝试为你的答案创建一个 .NET 小提琴并在他的问题中使用 OP 的示例数据,你会发现你所拥有的不会给出他的预期结果。
  • 你好,本。 Here 是。它给出了相同的结果。
  • 我认为 OP 希望结果的顺序与他在问题中指定的顺序完全相同。要实现该排序,您需要分组,按日期排序,然后应用不同的。我也有你的小提琴给出的结果集,但我认为 OP 想要指定的顺序;我的回答按顺序给出了结果。
  • 我明白了。在我看来,OP 没有这样的要求。
猜你喜欢
  • 2022-11-26
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2011-04-21
  • 2012-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多