item from example c# .net linq list

from - list.where c#



Usando LINQ para eliminar elementos de una lista<T> (15)

Digamos que tengo una consulta LINQ como:

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

Dado que la lista de authorsList es de tipo List<Author> , ¿cómo puedo eliminar los elementos de Author de la authorsList de authorsList que la consulta devuelve a los authors ?

O, dicho de otra manera, ¿cómo puedo eliminar todos los nombres de Bob de la authorsList de authorsList ?

Nota: Este es un ejemplo simplificado para los propósitos de la pregunta.


A continuación se muestra el ejemplo para eliminar el elemento de la lista.

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


Bueno, en primer lugar sería más fácil excluirlos:

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

Sin embargo, eso solo cambiaría el valor de authorsList lugar de eliminar a los autores de la colección anterior. Alternativamente, puede usar RemoveAll :

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

Si realmente necesitas hacerlo en base a otra colección, usaría un HashSet, RemoveAll y Contiene:

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


Creo que podrías hacer algo como esto

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

Aunque creo que las soluciones ya dadas resuelven el problema de una manera más legible.


Creo que solo tienes que asignar los elementos de la lista de autores a una nueva lista para tener ese efecto.

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


Es muy simple:

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


Esta es una pregunta muy antigua, pero encontré una manera muy simple de hacer esto:

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

Tenga en cuenta que, dado que la variable de devolución authorsList es una List<T> , el IEnumerable<T> devuelto por Except() debe convertirse en una List<T> .


Estaba vagando, si hay alguna diferencia entre RemoveAll y Except y las ventajas de usar HashSet, por lo que he realizado una rápida comprobación de rendimiento :)

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

Resultados a continuación:

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

Como podemos ver, la mejor opción en ese caso es usar RemoveAll (HashSet)


LINQ tiene sus orígenes en la programación funcional, que enfatiza la inmutabilidad de los objetos, por lo que no proporciona una forma integrada de actualizar la lista original en el lugar.

Nota sobre la inmutabilidad (tomada de otra respuesta SO):

Aquí está la definición de inmutabilidad de Wikipedia (enlace)

"En la programación orientada a objetos y funcional, un objeto inmutable es un objeto cuyo estado no puede modificarse después de su creación".


No puede hacer esto con operadores LINQ estándar porque LINQ proporciona consulta, no soporte de actualización.

Pero puedes generar una nueva lista y reemplazar la anterior.

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

O puede eliminar todos los elementos de los authors en una segunda pasada.

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


Para mantener el código fluido (si la optimización del código no es crucial) y necesitaría realizar algunas operaciones adicionales en la lista:

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

o

authorsList = authorsList.Where(x => !setToRemove.Contains(x)).<do_some_further_Linq>;


Se puede eliminar de dos maneras.

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

o

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

Tuve el mismo problema, si desea una salida simple basada en su condición, la primera solución es mejor.


Sería mejor usar RemoveAll para lograr esto.

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


Si realmente necesitas eliminar elementos, ¿qué pasa con Except ()?
Puede eliminar según una nueva lista, o eliminar sobre la marcha anidando el 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();


Solución simple:

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


Supongamos que authorsToRemove es un IEnumerable<T> que contiene los elementos que desea eliminar de authorsList .

Entonces aquí hay otra forma muy sencilla de realizar la tarea de eliminación solicitada por el OP:

authorsList.RemoveAll(authorsToRemove.Contains);