使用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();
请注意,由于返回variablesauthorsList
是List<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");