778 Stimmen

LINQ verwenden, um Elemente aus einer List<T> zu entfernen

Angenommen, ich habe LINQ-Abfrage wie:

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

Angesichts der Tatsache, dass authorsList ist vom Typ List<Author> wie kann ich die Author Elemente aus authorsList die von der Abfrage zurückgegeben werden, in authors ?

Oder, anders ausgedrückt, wie kann ich alle Vornamen, die gleich Bob sind, aus authorsList ?

Hinweis: Dies ist ein vereinfachtes Beispiel für die Zwecke der Frage.

23voto

suszig Punkte 310

Ich habe mich gefragt, ob es einen Unterschied gibt zwischen RemoveAll et Except und die Vorteile der Verwendung von HashSet also habe ich einen schnellen Leistungscheck durchgeführt :)

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

Ergebnisse unten:

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

Wie wir sehen können, ist die beste Option in diesem Fall die Verwendung von RemoveAll(HashSet)

12voto

Carlos Martinez T Punkte 6358

Dies ist eine sehr alte Frage, aber ich habe eine wirklich einfache Möglichkeit gefunden, dies zu tun:

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

Beachten Sie, dass die Rückgabevariable authorsList ist eine List<T> die IEnumerable<T> zurückgegeben von Except() muss in eine List<T> .

8voto

AsifQadri Punkte 2388

Sie können auf zwei Arten entfernen

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

oder

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

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

Ich hatte das gleiche Problem, wenn Sie einfache Ausgabe basierend auf Ihrem wo Bedingung wollen, dann erste Lösung ist besser.

7voto

Samuel Jack Punkte 31654

LINQ hat seinen Ursprung in der funktionalen Programmierung, bei der die Unveränderlichkeit von Objekten im Vordergrund steht, und bietet daher keine integrierte Möglichkeit, die ursprüngliche Liste an Ort und Stelle zu aktualisieren.

Anmerkung zur Unveränderlichkeit (aus einer anderen SO-Antwort):

Hier ist die Definition von Unveränderlichkeit aus Wikipedia .

In der objektorientierten und funktionalen Programmierung ist ein unveränderliches Objekt ein Objekt, dessen Zustand nach seiner Erstellung nicht verändert werden kann.

7voto

atconway Punkte 19796

Sagen Sie das authorsToRemove ist ein IEnumerable<T> die die Elemente enthält, die Sie aus authorsList .

Dann gibt es eine weitere, sehr einfache Möglichkeit, die vom Auftraggeber gestellte Aufgabe des Entfernens zu bewältigen:

authorsList.RemoveAll(authorsToRemove.Contains);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X