使用LINQ从List <T>中删除元素

说我有LINQ查询如:

var authors = from x in authorsList where x.firstname == "Bob" select x; 

鉴于authorsList的types是List<Author> ,我怎样才能从authorsList中删除由查询返回给Author元素?

或者authorsList一种说法,我怎样才能从authorsList删除所有名字相等的Bob?

注意:这是针对该问题的简化示例。

那么,首先排除它们会更容易:

 authorsList = authorsList.Where(x => x.FirstName != "Bob").ToList(); 

但是,这只会改变authorsList的值,而不是从以前的集合中删除作者。 或者,您可以使用RemoveAll

 authorsList.RemoveAll(x => x.FirstName == "Bob"); 

如果你真的需要做另一个集合,我会使用一个HashSet,RemoveAll和Contains:

 var setToRemove = new HashSet<Author>(authors); authorsList.RemoveAll(x => setToRemove.Contains(x)); 

最好使用List <T> .RemoveAll来实现这一点。

 authorsList.RemoveAll((x) => x.firstname == "Bob"); 

如果你真的需要删除项目那么Except()呢?
您可以基于新列表进行删除,或者通过嵌套Linq进行即时删除。

 var authorsList = new List<Author>() { new Author{ Firstname = "Bob", Lastname = "Smith" }, new Author{ Firstname = "Fred", Lastname = "Jones" }, new Author{ Firstname = "Brian", Lastname = "Brains" }, new Author{ Firstname = "Billy", Lastname = "TheKid" } }; var authors = authorsList.Where(a => a.Firstname == "Bob"); authorsList = authorsList.Except(authors).ToList(); authorsList = authorsList.Except(authorsList.Where(a=>a.Firstname=="Billy")).ToList(); 

你不能用标准的LINQ操作符来做这件事,因为LINQ提供查询,而不是更新支持。

但是你可以生成一个新的列表并replace旧的列表。

 var authorsList = GetAuthorList(); authorsList = authorsList.Where(a => a.FirstName != "Bob").ToList(); 

或者你可以在第二遍中删除authors所有项目。

 var authorsList = GetAuthorList(); var authors = authorsList.Where(a => a.FirstName == "Bob").ToList(); foreach (var author in authors) { authorList.Remove(author); } 

简单的scheme:

 static void Main() { List<string> myList = new List<string> { "Jason", "Bob", "Frank", "Bob" }; myList.RemoveAll(x => x == "Bob"); foreach (string s in myList) { // } } 

我在徘徊,如果RemoveAll和Except之间有什么区别和使用HashSet的优点,所以我做了快速性能检查:)

 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; namespace ListRemoveTest { class Program { private static Random random = new Random( (int)DateTime.Now.Ticks ); static void Main( string[] args ) { Console.WriteLine( "Be patient, generating data..." ); List<string> list = new List<string>(); List<string> toRemove = new List<string>(); for( int x=0; x < 1000000; x++ ) { string randString = RandomString( random.Next( 100 ) ); list.Add( randString ); if( random.Next( 1000 ) == 0 ) toRemove.Insert( 0, randString ); } List<string> l1 = new List<string>( list ); List<string> l2 = new List<string>( list ); List<string> l3 = new List<string>( list ); List<string> l4 = new List<string>( list ); Console.WriteLine( "Be patient, testing..." ); Stopwatch sw1 = Stopwatch.StartNew(); l1.RemoveAll( toRemove.Contains ); sw1.Stop(); Stopwatch sw2 = Stopwatch.StartNew(); l2.RemoveAll( new HashSet<string>( toRemove ).Contains ); sw2.Stop(); Stopwatch sw3 = Stopwatch.StartNew(); l3 = l3.Except( toRemove ).ToList(); sw3.Stop(); Stopwatch sw4 = Stopwatch.StartNew(); l4 = l4.Except( new HashSet<string>( toRemove ) ).ToList(); sw3.Stop(); Console.WriteLine( "L1.Len = {0}, Time taken: {1}ms", l1.Count, sw1.Elapsed.TotalMilliseconds ); Console.WriteLine( "L2.Len = {0}, Time taken: {1}ms", l1.Count, sw2.Elapsed.TotalMilliseconds ); Console.WriteLine( "L3.Len = {0}, Time taken: {1}ms", l1.Count, sw3.Elapsed.TotalMilliseconds ); Console.WriteLine( "L4.Len = {0}, Time taken: {1}ms", l1.Count, sw3.Elapsed.TotalMilliseconds ); Console.ReadKey(); } private static string RandomString( int size ) { StringBuilder builder = new StringBuilder(); char ch; for( int i = 0; i < size; i++ ) { ch = Convert.ToChar( Convert.ToInt32( Math.Floor( 26 * random.NextDouble() + 65 ) ) ); builder.Append( ch ); } return builder.ToString(); } } } 

结果如下:

 Be patient, generating data... Be patient, testing... L1.Len = 985263, Time taken: 13411.8648ms L2.Len = 985263, Time taken: 76.4042ms L3.Len = 985263, Time taken: 340.6933ms L4.Len = 985263, Time taken: 340.6933ms 

正如我们所见,在这种情况下最好的select是使用RemoveAll(HashSet)

这是一个非常古老的问题,但我发现了一个非常简单的方法来做到这一点:

 authorsList = authorsList.Except(authors).ToList(); 

请注意,由于返回variablesauthorsListList<T> ,因此Except()返回的IEnumerable<T>必须转换为List<T>

你可以用两种方法去除

 var output = from x in authorsList where x.firstname != "Bob" select x; 

要么

 var authors = from x in authorsList where x.firstname == "Bob" select x; var output = from x in authorsList where !authors.Contains(x) select x; 

我有同样的问题,如果你想根据你的条件简单的输出,那么第一个解决scheme是更好的。

authorsToRemove是一个IEnumerable<T> ,它包含你想从authorsList移除的元素。

那么这里是另一个非常简单的方法来完成OP所要求的删除任务:

 authorsList.RemoveAll(authorsToRemove.Contains); 

我想你可以做这样的事情

  authorsList = (from a in authorsList where !authors.Contains(a) select a).ToList(); 

尽pipe我认为已经给出的解决scheme以更可读的方式解决了这个问题。

以下是从列表中删除元素的示例。

  List<int> items = new List<int>() { 2, 2, 3, 4, 2, 7, 3,3,3}; var result = items.Remove(2);//Remove the first ocurence of matched elements and returns boolean value var result1 = items.RemoveAll(lst => lst == 3);// Remove all the matched elements and returns count of removed element items.RemoveAt(3);//Removes the elements at the specified index 

LINQ起源于函数式编程,强调对象的不可变性,所以它不提供内置的方式来原始地更新原始列表。

关于不变性的说明(摘自另一个SO答案):

这里是维基百科定义的不变性(链接)

“在面向对象和函数式编程中,不可变对象是一个在创build后状态不能被修改的对象。

我想你只需要将作者列表中的项目分配到新的列表中即可。

 //assume oldAuthor is the old list Author newAuthorList = (select x from oldAuthor where x.firstname!="Bob" select x).ToList(); oldAuthor = newAuthorList; newAuthorList = null; 

很简单:

 authorsList.RemoveAll((x) => x.firstname == "Bob");