it-swarm.com.de

Verwenden von LINQ zum Entfernen von Elementen aus einer Liste <T>

Angenommen, ich habe eine LINQ-Abfrage wie:

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

Wie kann ich angesichts der Tatsache, dass authorsList vom Typ List<Author> ist, die Author-Elemente aus authorsList löschen, die von der Abfrage in authors zurückgegeben werden?

Oder anders ausgedrückt, wie kann ich alle gleichwertigen Bob des Vornamens aus authorsList löschen?

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

574
TK.

Nun, es wäre einfacher, sie von vornherein auszuschließen:

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

Dies würde jedoch nur den Wert von authorsList ändern, anstatt die Autoren aus der vorherigen Sammlung zu entfernen. Alternativ können Sie RemoveAll verwenden:

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

Wenn Sie es wirklich tun müssen, basierend auf einer anderen Sammlung, würde ich HashSet, RemoveAll und Contains verwenden:

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

Um dies zu erreichen, ist es besser, List <T> .RemoveAll zu verwenden.

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

Wenn Sie wirklich Elemente entfernen müssen, was ist dann mit Except ()?
Sie können die Liste basierend auf einer neuen Liste entfernen oder durch Schachteln des Linq-Objekts entfernen.

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();
38
BlueChippy

Mit Standard-LINQ-Operatoren ist dies nicht möglich, da LINQ Unterstützung für Abfragen und keine Aktualisierung bietet.

Sie können jedoch eine neue Liste erstellen und die alte ersetzen.

var authorsList = GetAuthorList();

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

Oder Sie können alle Elemente in authors in einem zweiten Durchlauf entfernen.

var authorsList = GetAuthorList();

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

foreach (var author in authors)
{
    authorList.Remove(author);
}
24

Einfache Lösung:

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

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

Ich bin gewandert, wenn es Unterschiede zwischen RemoveAll und Except und den Profis der Verwendung von HashSet gibt, also habe ich einen schnellen Performance-Check 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 vonRemoveAll (HashSet)

14
suszig

Dies ist eine sehr alte Frage, aber ich habe einen wirklich einfachen Weg gefunden, dies zu tun: 

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

Da die Rückgabevariable authorsList ein List<T> ist, muss der von Except() zurückgegebene IEnumerable<T> in einen List<T> umgewandelt werden.

8

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 eine einfache Ausgabe basierend auf Ihrer Where-Bedingung wünschen, ist die erste Lösung besser.

7
AsifQadri

Angenommen, authorsToRemove ist ein IEnumerable<T>, der die Elemente enthält, die Sie aus authorsList entfernen möchten.

Dann ist eine weitere sehr einfache Möglichkeit, die vom OP angeforderte Entfernungsaufgabe auszuführen:

authorsList.RemoveAll(authorsToRemove.Contains);
6
atconway

Ich denke, du könntest so etwas tun

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

Obwohl ich denke, dass die bereits gegebenen Lösungen das Problem lesbarer lösen.

5
ebrown

Unten ist das Beispiel, um das Element aus der Liste zu entfernen.

 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
4

ich denke, Sie müssen nur die Elemente aus der Autorenliste einer neuen Liste zuweisen, um diesen Effekt zu erzielen.

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

Um den Code fließend zu halten (wenn die Codeoptimierung nicht entscheidend ist), müssen Sie einige weitere Vorgänge in der Liste durchführen:

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

oder

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

LINQ hat seinen Ursprung in der funktionalen Programmierung, die die Unveränderlichkeit von Objekten hervorhebt, und bietet daher keine integrierte Methode zum Aktualisieren der ursprünglichen Liste.

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

Hier ist die Definition der Unveränderlichkeit von Wikipedia (Link)

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

0
Samuel Jack