CSharp - 使用LINQ删除 List<T> 的元素

假设我有LINQ查询,比如:


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

鉴于 authorsList的类型为 List

或者,换句话说,如何删除 authorsList 中的所有内容?

时间:


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

但是,这将只更改 authorsList的值,而不是从先前的集合中删除authors。 或者,你可以使用 RemoveAll:


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

如果你确实需要根据其他集合进行操作,则可以使用HashSet,RemoveAll和Contains:


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


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



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();

简单的解决方案:


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

 foreach (string s in myList)
 {
//
 }
}

你不能使用标准的LINQ运算符来执行这里操作,因为LINQ提供查询,而不是更新。

但是你可以生成一个新的List 并替换旧的。


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);
}


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

我们可以看到,在这种情况下,最好的选择是使用 RemoveAll( HashSet )

你可以用两种方式删除


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;

如果你想要基于条件的简单输出,那么第一个解决方案更好。

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


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

请注意,由于将变量返回 authorsListList<T>,返回的IEnumerable<T>Except() 必须转换为 List<T>


authorsList.RemoveAll(authorsToRemove.Contains);

我觉得你可以这样做


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

解决方案以更易读的方式解决了这个问题。

...