c# predicate - Usando LINQ per rimuovere elementi da un elenco<T>




from item (13)

Sarebbe meglio usare RemoveAll per ottenere questo risultato.

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

Dì che ho una query LINQ come:

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

Dato che authorsList è di tipo List<Author> , come posso eliminare gli elementi Author da authorsList che vengono restituiti dalla query agli authors ?

O, per dirla in altro modo, come posso eliminare tutti gli equivalenti Bob del primo nome da authorsList ?

Nota: questo è un esempio semplificato ai fini della domanda.


Questa è una domanda molto vecchia, ma ho trovato un modo molto semplice per farlo:

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

Si noti che poiché la variabile return authorsList è una List<T> , IEnumerable<T> restituito da Except() deve essere convertito in una List<T> .


Puoi rimuovere in due modi

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;

Ho avuto lo stesso problema, se si desidera un output semplice basato sulla condizione in cui si trova, quindi la prima soluzione è migliore.


Bene, sarebbe più facile escluderli in primo luogo:

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

Tuttavia, ciò cambierebbe semplicemente il valore di authorsList invece di rimuovere gli autori dalla raccolta precedente. In alternativa, puoi usare RemoveAll :

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

Se hai davvero bisogno di farlo in base a un'altra raccolta, utilizzerei un HashSet, RemoveAll e Contains:

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

Se hai davvero bisogno di rimuovere elementi, allora che dire di Except ()?
È possibile rimuovere in base a un nuovo elenco o rimuovere al volo nidificando il 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();

È molto semplice:

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

Soluzione semplice:

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

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

LINQ ha le sue origini nella programmazione funzionale, che enfatizza l'immutabilità degli oggetti, quindi non fornisce un modo integrato per aggiornare l'elenco originale sul posto.

Nota sull'immutabilità (presa da un'altra risposta SO):

Ecco la definizione di immutabilità da Wikipedia (link)

"Nella programmazione orientata agli oggetti e funzionale, un oggetto immutabile è un oggetto il cui stato non può essere modificato dopo la sua creazione."


Penso che devi solo assegnare gli articoli dalla lista degli autori a una nuova lista per ottenere quell'effetto.

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

Stavo vagando, se c'è qualche differenza tra RemoveAll ed Except e i pro dell'utilizzo di HashSet, così ho fatto un rapido controllo delle prestazioni :)

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

Risultati di seguito:

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

Come possiamo vedere, l'opzione migliore in questo caso è usare RemoveAll (HashSet)


Penso che potresti fare qualcosa di simile

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

Anche se penso che le soluzioni già fornite risolvano il problema in un modo più leggibile.


Dì che authorsToRemove è un IEnumerable<T> che contiene gli elementi che vuoi rimuovere da authorsList .

Quindi ecco un altro modo molto semplice per eseguire l'operazione di rimozione richiesta dall'OP:

authorsList.RemoveAll(authorsToRemove.Contains);

Alcuni metodi che utilizzo:

if not a:
    print "list is empty"


if len(a) == 0:
    print "list is empty"




c# .net linq list