【问题标题】:Remove non-common items from list从列表中删除不常见的项目
【发布时间】:2013-09-18 14:43:05
【问题描述】:

我有两个对象列表,其中一个具有不同数量的项目。现在,如果我这样做:

 var result = list1.Except(list2);

这会让我区分 list1 和 list2 中的项目,对吧?如果可能的话,我想做的是在同一步骤中从 list1 中删除所有这些项目。 我不想做的是必须遍历“结果”列表并将它们从 list1 中删除。这可能吗?

谢谢!

【问题讨论】:

  • 我实际上试过这个:list1.RemoveAll(list1.Except(list2));这给出了一个错误
  • @LuisGarcia - 您在寻找单行语句吗?为什么厌恶循环?它很容易理解。

标签: c# linq


【解决方案1】:

你可以使用List.RemoveAll:

list1.RemoveAll(t => !list2.Contains(t));

这不需要创建一个新的集合,它也只需要一个循环来删除所有在list1中但不在list2中的项目。但是,我假设您误解了 LINQ 的工作原理。 Enumerable.Except 是使用延迟执行实现的。这意味着它不会在foreach 之前执行。 Except 对于大型列表也非常有效,因为它使用了集合方法。

【讨论】:

    【解决方案2】:

    Tim 的解决方案是正确的,但运行时间为 O(list1.Length * list2.Length)。当您使用带有适当哈希的 HashSet<T> 时,您将接近 O(O(list1.Length + list2.Length) 运行时。当 list2 包含多个项目时,这会快得多。

    我的变体的缺点是它需要分配哈希集,因此需要更多内存。

    var set2 = new HashSet<T>(list2);
    list1.RemoveAll(item=>!set2.Contains(item));
    

    【讨论】:

    • 如果内存无关紧要,这是最有效的方法,所以 +1。
    【解决方案3】:

    Tim's solution 的效率较低的替代方法是简单的Where 子句(有点像 SQL 中的子查询)。好处是您不需要修改原始集合:

    string[] list1 = new string[] { "a", "b", "c", "d", "e" };
    string[] list2 = new string[] { "c", "d", "e", "f", "g" };
    
    var result = list1.Where( i => list2.Contains( i ) ).ToArray();
    

    如果您有非常大的数据集,我仍然会推荐 Tim 的方法。

    结果:

    c
    d
    e

    要使用 SQL 术语(帮助您进行谷歌搜索),这称为“内连接”。在您的情况下,这是一个非常简单的问题,因为您的输入列表和结果列表属于同一类型。如果您想在不同类型的列表之间进行更复杂的连接,您可能会发现这个问题很有帮助:

    inner join in linq to entities

    【讨论】:

    • 这效率不高。它与我在大型列表中的方法具有相同的缺点,因为它的复杂性是O(list1.Length * list2.Length)。但它还必须创建一个新数组(注意ToListToArray 更有效,OP 无论如何都使用了列表)。 OP 也没有使用 Linq-To-Sql 而是使用 Linq-To-Objects 作为Except 的建议。
    • @TimSchmelter - 我已修改我的帖子以解决您的问题。我并不是说它更有效。此外,虽然这篇文章是关于 LINQ to SQL 的,但语法对 LINQ to Entities 同样有效。我发现了一个更(表面上)主题的不同帖子
    猜你喜欢
    • 2019-05-24
    • 2023-01-27
    • 1970-01-01
    • 2022-12-20
    • 1970-01-01
    • 2013-01-17
    • 2021-05-31
    • 2016-05-08
    • 2011-03-18
    相关资源
    最近更新 更多