Uso de LINQ para eliminar elementos de una lista


Digamos que tengo una consulta LINQ como:

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

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

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

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

Author: John M, 2009-05-12

15 answers

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

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

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

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

Si realmente necesitas hacerlo basado en otra colección, usaría un HashSet, removeAll y Contains:

var setToRemove = new HashSet<Author>(authors);
authorsList.RemoveAll(x => setToRemove.Contains(x));
 951
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-12 16:01:39

Sería mejor usar List.removeAll para lograr esto.

authorsList.RemoveAll((x) => x.firstname == "Bob");
 115
Author: Reed Copsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-12 16:03:27

Si realmente necesita eliminar elementos, ¿qué pasa con Except()?
Puede eliminar basándose en 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();
 36
Author: BlueChippy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-12 16:01:36

No puede hacer esto con operadores LINQ estándar porque LINQ proporciona soporte para consultas, no para actualizaciones.

Pero puede 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 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);
}
 21
Author: Daniel Brückner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-12 16:02:43

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)
    {
        //
    }
}
 18
Author: CodeLikeBeaker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-03 18:40:50

Estaba vagando, si hay alguna diferencia entre removeAll y Except y pros de usar HashSet, por lo que he hecho quick performance check:)

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 utilizar removeAll (HashSet )

 13
Author: suszig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-26 16:38:44

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 retorno authorsList es un List<T>, el IEnumerable<T> devuelto por Except() debe convertirse a un List<T>.

 7
Author: Carlos Martinez T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-29 02:53:38

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 where , entonces la primera solución es mejor.

 6
Author: AsifQadri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-12 16:02:12

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

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

authorsList.RemoveAll(authorsToRemove.Contains);
 5
Author: atconway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-29 02:38:48

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.

 4
Author: ebrown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-12 16:41:12

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
 3
Author: Sheo Dayal Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-25 18:19:09

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 su lugar.

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

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

"En programación orientada a objetos y funcional, un objeto inmutable es un objeto cuyo estado no se puede modificar después de ser creado."

 0
Author: Samuel Jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 19:53:09

Creo que solo tiene 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;
 0
Author: aj go,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-28 05:36:21

Para mantener el código fluido (si la optimización del código no es crucial) y tendría que hacer 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>;
 0
Author: Zbigniew Wiadro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-15 13:48:45

Es muy simple:

authorsList.RemoveAll((x) => x.firstname == "Bob");
 -2
Author: Sandro Z.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-24 12:06:15