从另一个列表中按照条件删除列表项

60

我有一个写作者列表。

public class Writers{   
    long WriterID { get;set; }
}

我也有两个类型为文章(Article)的列表。

public class Article{
    long ArticleID { get; set; }
    long WriterID { get; set; }
    //and others    
}

所以我现有的代码是:

List<Article> ArticleList = GetList(1);
List<Article> AnotherArticleList = AnotherList(2);
List<Writers> listWriters = GetAllForbiddenWriters();

我想从ArticleListAnotherArticleList中删除那些WriterIDlistWriters中的WriterID匹配的记录。如何在LINQ中实现?

5个回答

118

如果你已经有一个 List<T>,我建议你在构建一组writer IDs后使用List<T>.RemoveAll

HashSet<long> writerIds = new HashSet<long>(listWriters.Select(x => x.WriterID));

articleList.RemoveAll(x => writerIds.Contains(x.WriterId));
anotherArticleList.RemoveAll(x => writerIds.Contains(x.WriterId));

如果你确实想要使用LINQ,那么可以使用以下方法:
articleList = articleList.Where(x => !writerIds.Contains(x.WriterId))
                         .ToList();
anotherArticleList = anotherArticleList
                         .Where(x => !writerIds.Contains(x.WriterId))
                         .ToList();

请注意,这会改变变量但不修改现有列表 - 因此,如果存在对同一列表的任何其他引用,则它们将看不到任何更改。 (而RemoveAll会修改现有列表。)

2
@bitxwise:不会,因为 HashSet 上的 Contains 不需要遍历整个集合。它执行哈希查找,其时间复杂度为 O(1),而不是 O(n)。此外,在每次迭代中,您也不必获取每个 Writer 的属性。就清晰度而言,我认为将“被禁止”的作者视为一个集合而不是列表更好,因为顺序不重要,我们通过 ID 查找 它们,这就是我们关心的全部。 - Jon Skeet
@Jon Skeet 如果我仍然需要列表的元素,从性能角度来看,将它们复制到一个新列表中使用 list<Article> tempArticleList = new list<Artice>(articleList); 然后对 TempList 使用 removeAll,哪种方式更好?谢谢你的支持。 - nayef harb
@nayefharb:说实话,我需要看到确切的上下文。 - Jon Skeet
@JonSkeet 你能看一下这个吗 https://gist.github.com/anonymous/87b8c5aec00ba38d6135#file-gistfile1-vb - nayef harb
@nayefharb:这会在每个步骤上创建一个临时集合,然后什么也不做——所以它远非有用的上下文。考虑提出一个更多上下文的新问题。 - Jon Skeet
显示剩余8条评论

50
articlesList.RemoveAll(a => listWriters.Exists(w => w.WriterID == a.WriterID));
anotherArticlesList.RemoveAll(a => listWriters.Exists(w => w.WriterID == a.WriterID));

7
您可以使用 Except
List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
List<car> result = list2.Except(list1).ToList();

1

我并不真正明白你面临的困难是什么...

为什么不使用简单的for循环来过滤/删除列表中的数据呢? (请注意,如果您在迭代时编辑/更改被迭代的对象,则foreach循环绝对不起作用)

for (int i = ArticleList.Count -1; i >= 0; i--)
{
    for (int j = 0; j < listWriters.Count; j++)
    {
        if (ArticleList[i].WriterId == listWriters[j].WriterID )
            ArticleList.RemoveAt(i);
    }            
}

反向迭代技巧解决了“在迭代时删除项目”的范式。


2
谢谢回答。实际上我想要一个LINQ解决方案。 - developer
3
我非常高兴不再需要编写那样的代码。告别 C/C++ 的日子,欢迎 Linq 的到来 - 天啊 - 我的眼睛都要出血了 :) - Piotr Kula
在C++中也不需要像那样编写代码。在C++中,您可以使用remove_if(很久以前引入)和lambda表达式,因此如果在C++中完成,当前接受的答案将是类似的。 - Marian Spanik

-2

一个设计提示,你的类应该叫做Writer(单数形式),而不是Writers(复数形式)。你列表中的每个项目都代表一个单独的作者,对吗?


25
你应该将这句话作为评论发布,因为它不是问题的答案。 - Stefan Steinegger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接