【问题标题】:How to find duplicates fieldsa in 2 lists of different types and remove them from both lists, in C#如何在 C# 中查找 2 个不同类型列表中的重复字段a 并将它们从两个列表中删除
【发布时间】:2019-08-21 13:06:41
【问题描述】:

这不是 Given 2 C# Lists how to merge them and get only the non duplicated elements from both lists 的副本,因为他正在查看相同类型的列表。

我有这种情况:

class A
{
    string id;
    .... some other stuff
}

class B
{
    string id;
    .... some other stuff
}

我想从 A 和 B 中删除在两个列表之间共享一个 id 字段的元素。

我可以分 3 步完成:找到公共 id,然后从两个列表中删除记录,但我想知道是否有更优雅的方法。


编辑:预期输出

var A = [ 1, 3, 5, 7, 9 ] 变量 B = [ 1, 2, 3, 4, 5 ]

输出:

A = [ 7, 9 ] B = [ 2, 4 ]

但这仅显示 id 字段;如上所述,列表是不同类型的,它们只是共享 id。

【问题讨论】:

  • 能否提供输入和预期输出
  • 为一个/每个类编写您自己的IComparer 实现(参见support.microsoft.com/en-us/help/320727/…
  • id是字符串,比较没问题;问题是是否有办法在不执行 3 个步骤的情况下做到这一点
  • 始终是 3 个步骤,但有一些方法可以通过使用 HashSets 来提高这些步骤的效率。为什么不展示你目前的表现。注意:由于您正在寻找“更好”的方式,这可能是codereview.stackexchange.com 上的一个更好的问题
  • 计算机编程就是把问题分解成更小的问题,解决这些问题,然后组合解决方案。你已经做到了。你完成了!如果您希望您的三个步骤显示为一个步骤,请创建一个方法,将这三个步骤放入方法中,然后调用该方法,调用者会看到这是一个步骤。

标签: c#


【解决方案1】:

您将需要三个步骤,但您可以使用 Linq 来简化代码。

给定两个具有相同(等价)类型的属性的类,命名为“ID”:

class Test1
{
    public string ID { get; set; }
}

class Test2
{
    public string ID { get; set; }
}

然后您可以找到重复项并将它们从两个列表中删除,如下所示:

var dups =
    (from item1 in list1
    join item2 in list2 on item1.ID equals item2.ID
    select item1.ID)
   .ToArray();

list1.RemoveAll(item => dups.Contains(item.ID));
list2.RemoveAll(item => dups.Contains(item.ID));

但这仍然是三个步骤。

有关可运行示例,请参阅 .Net Fiddle example

【讨论】:

    【解决方案2】:

    您可以使用 LINQ Lambda 表达式来获得优雅:

    var intersectValues = list2.Select(r => r.Id).Intersect(list1.Select(r => r.Id)).ToList();
    list1.RemoveAll(r => intersectValues.Contains(r.Id));
    list2.RemoveAll(r => intersectValues.Contains(r.Id));
    

    【讨论】:

      【解决方案3】:

      以@Matthew Watson 的回答为基础,您可以将其全部移至单个 LINQ 表达式

      (from item1 in list1
       join item2 in list2 on item1.ID equals item2.ID
       select item1.ID)
         .ToList()
           .ForEach(d => 
           {
               list1.RemoveAll(i1 => d == i1.ID);
               list2.RemoveAll(i2 => d == i2.ID);
           }
      );
      

      我不知道您在性能等级上的位置。编译器实际上可能会将其拆分为您已经提到的三个步骤。

      您还会失去一些可读性,因为 from ... select 结果没有像 duplicates 这样的“说话”名称,直接告诉您将在 ForEach 中处理什么。 p>

      完整的代码示例在https://gist.github.com/msdeibel/d2f8a97b754cca85fe4bcac130851597

      【讨论】:

        【解决方案4】:

        O(n)

                var aHash = list<A>.ToHashSet(x=>x.ID);
                var bHash = list<B>.ToHashSet(x=>x.ID);
                var result1 = new List<A>(A.Count);
                var result2 = new List<B>(B.Count);
                int value;
                foreach (A item in list<A>)
                {
                    if (!bHash.TryGetValue(item.ID, out value))
                        result1.Add(A);
                }
                foreach (B item in list<B>)
                {
                    if (!aHash.TryGetValue(item.ID, out value))
                        result2.Add(B);
                }
        

        【讨论】:

        • 这会更慢,因为它会遍历每个列表 2 次。
        • @Thomas 当列表大小为 100000 时,此方法需要 20 毫秒,其他方法需要 3500 毫秒
        • 你排除了第一遍吗? linq 在第一次执行时总是要贵得多,因为它必须编译表达式。如果是这样,这会很有趣,我会做一些测试
        • @Thomas 因为RemoveAll比较时需要O(n)次,而HashSet比较只需要O(1)次,这种方法在大数据时会更快
        猜你喜欢
        • 2021-12-09
        • 2019-11-22
        • 2018-11-23
        • 1970-01-01
        • 2020-06-05
        • 2019-03-17
        • 1970-01-01
        • 1970-01-01
        • 2017-03-24
        相关资源
        最近更新 更多