13 Stimmen

Entfernen von Objekten aus einer Enumerable-Sammlung in einer Schleife

Duplizieren Sie

Ändern einer Sammlung beim Iterieren durch die Sammlung


Hat jemand ein nettes Muster, damit ich um die Unfähigkeit, Objekte zu entfernen, während ich Schleife durch eine aufzählbare Sammlung (z. B. eine IList oder KeyValuePairs in einem Wörterbuch) erhalten

Das folgende Beispiel schlägt fehl, da es die Liste, die während der foreach-Aufzählung aufgezählt wird, modifiziert

foreach (MyObject myObject in MyListOfMyObjects)
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

In der Vergangenheit habe ich zwei Methoden angewandt.

Ich habe die foreach-Schleife durch eine umgekehrte for-Schleife ersetzt (um die Indizes nicht zu ändern, über die ich eine Schleife mache, wenn ich ein Objekt entferne).

Ich habe auch versucht, eine neue Sammlung von Objekten zu speichern, die innerhalb der Schleife zu entfernen sind, und dann eine Schleife durch diese Sammlung zu ziehen und die Objekte aus der ursprünglichen Sammlung zu entfernen.

Diese funktionieren gut, aber weder fühlt sich schön, und ich habe mich gefragt, ob jemand eine bessere Lösung gefunden hat. elegant Lösung des Problems

0 Stimmen

Ich scheine meinen eigenen Beitrag nicht schließen zu können, da SO denkt, dass ich versuche, über ihn abzustimmen. Ich freue mich, wenn das jemand anderes tut.

0 Stimmen

Das ist kein Problem. Ich verbringe mehrere Minuten damit, SO zu suchen, bevor ich eine Frage stelle. An die ursprüngliche Frage erinnere ich mich, aber sie war nicht sehr gut betitelt. Ich habe sie bearbeitet, so dass sie in Zukunft leichter zu finden sein sollte: Wie ändert oder löscht man Elemente aus einer aufzählbaren Sammlung, während man sie in C# durchläuft?

13voto

ChrisW Punkte 53239

Es gibt eine nützliche List<T>.RemoveAll(Predicate<T> match) Methode, die meines Erachtens für diesen Zweck gedacht ist: http://msdn.microsoft.com/en-us/library/wdka673a.aspx

0 Stimmen

Die bearbeitete Antwort ist sogar noch schöner :), obwohl meine derzeitige Anforderung darin besteht, Elemente aus einem Wörterbuch zu entfernen, das weder RemoveRange noch RemoveAll hat, also werde ich einfach die Methode mit der doppelten Schleife anwenden

0 Stimmen

Wenn es sich um ein Wörterbuch handelt, verwenden Sie vielleicht die Schlüssel des Wörterbuchs, um eine Liste oder ein Array zu initialisieren (d.h. kopieren Sie alle Schlüssel in eine zweite Auflistung): und durchlaufen Sie dann diese zweite Auflistung und entfernen Sie Einträge aus dem Wörterbuch.

0 Stimmen

Genau das habe ich getan, danke.

8voto

Mark Maxham Punkte 1461

Es ist ein bisschen einfältig, aber wenn ich plane, Elemente aus einer IEnumerable/IList zu löschen, mache ich normalerweise einfach eine Kopie:

foreach (MyObject myObject in new List<MyObject>(MyListOfMyObjects))
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

Das ist nicht die effizienteste Methode, aber sie ist leicht zu lesen. Vorzeitige Optimierung und so weiter.

2voto

TheSoftwareJedi Punkte 33452

In umgekehrter Weise erstellen Sie eine neue Liste:

List myFilteredList = new List();
foreach (MyObject myObject in myListOfMyObjects)
{
     if (!condition) myFilteredList.Add(myObject);
}

Verwenden Sie dann die neue Liste, wo immer Sie sie brauchen.

Sie können auch einfach einen LINQ-Ausdruck verwenden, indem Sie die Bedingung umkehren. Dies hat den zusätzlichen Vorteil, dass keine neue Struktur erstellt wird, aber auch den Nachteil, dass es sich um eine "Lazy Enumerable" handelt:

var myFilteredList = from myObject in myListOfMyObjects
                     where !condition
                     select myObject;

Wenn Sie die Elemente jedoch wirklich aus der Liste entfernen müssen, verwende ich normalerweise den Ansatz "eine neue Liste erstellen, dann wiederholen und entfernen".

2voto

Rob Walker Punkte 45267

Die Idee der umgekehrten for-Schleife gefällt mir nicht, da dies nur bei bestimmten Datenstrukturen funktioniert.

Im Allgemeinen würde ich die zweite Methode anwenden und die zu löschenden Elemente in einer separaten "zu löschenden" Sammlung zusammenfassen. Wenn das Löschen dazu führen kann, dass bestehende Iterate ungültig werden (wie es z. B. bei jeder ausgeglichenen Baumsammlung der Fall ist), sehe ich keine Möglichkeit, dies zu umgehen.

Die einzige andere Technik, die ich gelegentlich verwendet habe, besteht darin, die gesamte Iteration neu zu starten, wenn Sie das erste zu löschende Element finden. Wenn Sie es schaffen, ohne ein zu löschendes Element zu finden, ist die Funktion beendet. Das ist ineffizient, aber manchmal notwendig, wenn sich durch das Löschen eines Elements aus der Sammlung die Menge der zu löschenden Elemente ändern kann.

2voto

calvin Punkte 21

Ich bin gerade auf diesen Beitrag gestoßen und dachte, ich würde ihn mit Ihnen teilen.

void RemoveAll(object condition)  
{

    bool found = false;

    foreach(object thisObject in objects)    
    {

        if (condition)    
        {    
            objects.Remove(thisObject);

            found = true;

            break; //exit loop    
        }     
     }

    // Call again recursively

    if (found) RemoveAll(condition);

}

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