it-swarm.com.de

Bester Weg, um Elemente aus einer Sammlung zu entfernen

Was ist der beste Weg, um Elemente aus einer Sammlung in C # zu entfernen, sobald das Element bekannt ist, nicht jedoch dessen Index. Dies ist eine Möglichkeit, dies zu tun, scheint jedoch bestenfalls unelegant zu sein.

//Remove the existing role assignment for the user.
int cnt = 0;
int assToDelete = 0;
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
    if (spAssignment.Member.Name == shortName)
    {
        assToDelete = cnt;
    }
    cnt++;
}
workspace.RoleAssignments.Remove(assToDelete);

Was ich wirklich tun möchte, ist, das Element nach der Eigenschaft (in diesem Fall dem Namen) zu entfernen, ohne die gesamte Sammlung zu durchlaufen und 2 zusätzliche Variablen zu verwenden.

65
Dan

Wenn Sie über eine ihrer Eigenschaften auf Mitglieder der Sammlung zugreifen möchten, können Sie stattdessen Dictionary<T> oder KeyedCollection<T> verwenden. Auf diese Weise müssen Sie nicht nach dem gesuchten Artikel suchen.

Ansonsten könntest du das zumindest tun:

foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
    if (spAssignment.Member.Name == shortName)
    {
        workspace.RoleAssignments.Remove(spAssignment);
        break;
    }
}
25
Jon B

Wenn RoleAssignments ein List<T> ist, können Sie den folgenden Code verwenden.

workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
126
JaredPar

@smaclell fragte, warum die umgekehrte Iteration in einem Kommentar zu @ sambo99 effizienter sei.

Manchmal es ist effizienter. Stellen Sie sich vor, Sie haben eine Liste von Personen, und Sie möchten alle Kunden mit einer Bonität <1000 entfernen oder filtern.

Wir haben folgende Daten

"Bob" 999
"Mary" 999
"Ted" 1000

Wenn wir vorwärts iterieren würden, würden wir bald in Schwierigkeiten geraten

for( int idx = 0; idx < list.Count ; idx++ )
{
    if( list[idx].Rating < 1000 )
    {
        list.RemoveAt(idx); // whoops!
    }
}

Bei idx = 0 entfernen wir Bob, wodurch alle verbleibenden Elemente verschoben werden. Beim nächsten Mal durch die Schleife idx = 1, aber list [1] ist jetzt Ted anstelle von Mary. Am Ende überspringen wir Mary aus Versehen. Wir könnten eine while-Schleife verwenden und weitere Variablen einführen.

Oder wir kehren einfach um: 

for (int idx = list.Count-1; idx >= 0; idx--)
{
    if (list[idx].Rating < 1000)
    {
        list.RemoveAt(idx);
    }
}

Alle Indizes links vom entfernten Element bleiben unverändert, sodass Sie keine Elemente überspringen.

Dasselbe Prinzip gilt, wenn Sie eine Liste von Indizes erhalten, die aus einem Array entfernt werden sollen. Um den Überblick zu behalten, müssen Sie die Liste sortieren und dann die Elemente vom höchsten zum niedrigsten Index entfernen.

Jetzt können Sie einfach Linq verwenden und auf einfache Weise erklären, was Sie tun.

list.RemoveAll(o => o.Rating < 1000);

In diesem Fall des Entfernens eines einzelnen Elements ist es nicht effizienter, vorwärts oder rückwärts zu iterieren. Sie könnten auch Linq dafür verwenden.

int removeIndex = list.FindIndex(o => o.Name == "Ted");
if( removeIndex != -1 )
{
    list.RemoveAt(removeIndex);
}
21
Robert Paulson

Für eine einfache Listenstruktur scheint die Predicate RemoveAll-Implementierung der effizienteste Weg zu sein. 

Z.B.

 workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);

Die Gründe sind: 

  1. Die Predicate/Linq RemoveAll-Methode ist in List implementiert und hat Zugriff auf das interne Array, in dem die tatsächlichen Daten gespeichert sind. Dadurch werden die Daten verschoben und die Größe des internen Arrays geändert.
  2. Die Implementierung der RemoveAt-Methode ist ziemlich langsam und kopiert das gesamte zugrunde liegende Datenarray in ein neues Array. Dies bedeutet, dass die umgekehrte Iteration für List unbrauchbar ist 

Wenn Sie stecken bleiben, implementieren Sie dies in einer Ära vor c # 3.0. Sie haben 2 Möglichkeiten. 

  • Die leicht zu wartende Option. Kopieren Sie alle übereinstimmenden Elemente in eine neue Liste und tauschen Sie die zugrunde liegende Liste aus. 

Z.B. 

List<int> list2 = new List<int>() ; 
foreach (int i in GetList())
{
    if (!(i % 2 == 0))
    {
        list2.Add(i);
    }
}
list2 = list2;

Oder 

  • Die etwas schnellere Option, bei der alle Daten in der Liste nach unten verschoben werden, wenn sie nicht übereinstimmen, und dann die Größe des Arrays ändern. 

Wenn Sie wirklich häufig Dinge aus einer Liste entfernen, möglicherweise eine andere Struktur wie HashTable (.net 1.1) oder ein Dictionary (.net 2.0) oder ein HashSet (.net 3.5) sind besser für diesen Zweck geeignet. 

9
Sam Saffron

Wenn es sich um eine ICollection handelt, gibt es keine RemoveAll-Methode. Hier ist eine Erweiterungsmethode, die es tun wird:

    public static void RemoveAll<T>(this ICollection<T> source, 
                                    Func<T, bool> predicate)
    {
        if (source == null)
            throw new ArgumentNullException("source", "source is null.");

        if (predicate == null)
            throw new ArgumentNullException("predicate", "predicate is null.");

        source.Where(predicate).ToList().ForEach(e => source.Remove(e));
    }

Basierend auf: http://phejndorf.wordpress.com/2011/03/09/a-removeall-extension-for-the-collectionclass/

8
Colin

Welcher Typ ist die Sammlung? Wenn es sich um eine Liste handelt, können Sie die hilfreiche "RemoveAll" verwenden:

int cnt = workspace.RoleAssignments
                      .RemoveAll(spa => spa.Member.Name == shortName)

(Dies funktioniert in .NET 2.0. Wenn Sie nicht über den neueren Compiler verfügen, müssen Sie natürlich "delegate (SPRoleAssignment spa) {return spa.Member.Name == shortName;}" anstelle von Nice verwenden Lambda-Syntax.)

Ein anderer Ansatz, wenn es sich nicht um eine Liste handelt, sondern um eine ICollection:

   var toRemove = workspace.RoleAssignments
                              .FirstOrDefault(spa => spa.Member.Name == shortName)
   if (toRemove != null) workspace.RoleAssignments.Remove(toRemove);

Dies erfordert die Enumerable-Erweiterungsmethoden. (Sie können die Mono-Monitore kopieren, wenn Sie an .NET 2.0 hängen bleiben). Wenn es sich um eine benutzerdefinierte Sammlung handelt, die kein Element aufnehmen kann, MÜSSEN jedoch ein Index, MÜSSEN einige der anderen Enumerable-Methoden, z. B. Select, den Ganzzahlindex für Sie übergeben. 

7
MichaelGG

Hier ist ein ziemlich guter Weg, dies zu tun

http://support.Microsoft.com/kb/555972

        System.Collections.ArrayList arr = new System.Collections.ArrayList();
        arr.Add("1");
        arr.Add("2");
        arr.Add("3");

        /*This throws an exception
        foreach (string s in arr)
        {
            arr.Remove(s);
        }
        */

        //where as this works correctly
        Console.WriteLine(arr.Count);
        foreach (string s in new System.Collections.ArrayList(arr)) 
        {
            arr.Remove(s);
        }
        Console.WriteLine(arr.Count);
        Console.ReadKey();
2
Jb

Dies ist meine generische Lösung

public static IEnumerable<T> Remove<T>(this IEnumerable<T> items, Func<T, bool> match)
    {
        var list = items.ToList();
        for (int idx = 0; idx < list.Count(); idx++)
        {
            if (match(list[idx]))
            {
                list.RemoveAt(idx);
                idx--; // the list is 1 item shorter
            }
        }
        return list.AsEnumerable();
    }

Es würde viel einfacher aussehen, wenn Erweiterungsmethoden das Übergeben durch Verweis unterstützen!

var result = string[]{"mike", "john", "ALi"}
result = result.Remove(x => x.Username == "mike").ToArray();
Assert.IsTrue(result.Length == 2);

BEARBEITEN: Es wurde sichergestellt, dass die Listenschleife auch beim Löschen von Elementen gültig bleibt, indem der Index (idx) dekrementiert wird.

2
Jalal El-Shaer

Um dies zu tun, während Sie die Sammlung durchlaufen und nicht die Änderung einer Sammlungsausnahme erhalten, ist dies der Ansatz, den ich in der Vergangenheit gewählt habe (beachten Sie die .ToList () am Ende der Originalsammlung, dadurch wird eine weitere Sammlung im Speicher erstellt dann können Sie die vorhandene Sammlung ändern)

foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments.ToList()) { if (spAssignment.Member.Name == shortName) { workspace.RoleAssignments.Remove(spAssignment); } }

0
Anthony Shaw

Am besten geht es mit linq. 

Beispielklasse:

 public class Product
    {
        public string Name { get; set; }
        public string Price { get; set; }      
    }

Linq-Abfrage:

var subCollection = collection1.RemoveAll(w => collection2.Any(q => q.Name == w.Name));

Diese Abfrage entfernt alle Elemente aus collection1, wenn Name mit irgendeinem Element Name aus collection2 übereinstimmt

Denken Sie daran zu verwenden: using System.Linq;

0
john.kernel

Viele gute Antworten hier; Ich mag besonders die Lambda-Ausdrücke ... sehr sauber. Ich war jedoch nachlässig, die Art der Sammlung nicht anzugeben. Dies ist eine SPRoleAssignmentCollection (von MOSS), die nur Remove (int) und Remove (SPPrincipal) enthält, nicht die praktische RemoveAll (). Also habe ich mich dazu entschieden, es sei denn, es gibt einen besseren Vorschlag.

foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
                        {
                            if (spAssignment.Member.Name != shortName) continue;
                            workspace.RoleAssignments.Remove((SPPrincipal)spAssignment.Member);
                            break;
                        }
0
Dan

Ähnlich wie bei Dictionary Collection habe ich dies getan.

Dictionary<string, bool> sourceDict = new Dictionary<string, bool>();
sourceDict.Add("Sai", true);
sourceDict.Add("Sri", false);
sourceDict.Add("SaiSri", true);
sourceDict.Add("SaiSriMahi", true);

var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false);

foreach (var item in itemsToDelete)
{
    sourceDict.Remove(item.Key);
}

Hinweis: Der obige Code schlägt im .NET-Client-Profil (3.5 und 4.5) fehl. Einige Zuschauer erwähnten, dass es .__ ist. Wenn in .Net4.0 dies nicht der Fall ist, sind die Einstellungen nicht sicher, welche Einstellungen das Problem verursachen .

Ersetzen Sie daher den folgenden Code (.ToList ()) für die Where-Anweisung, um diesen Fehler zu vermeiden. „Sammlung wurde geändert; Enumerationsoperation wird möglicherweise nicht ausgeführt. "

var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false).ToList();

Per MSDN Ab .Net4.5 wird das Client-Profil eingestellt. http://msdn.Microsoft.com/de-de/library/cc656912(v=vs.110).aspx

0
Sai

Je nachdem, wie Sie Ihre Sammlung verwenden, gibt es einen anderen Ansatz. Wenn Sie die Zuweisungen einmal herunterladen (z. B. wenn die App ausgeführt wird), können Sie die Sammlung sofort in eine Hashtabelle übersetzen, in der Folgendes möglich ist:

kurzname => SPRoleAssignment

Wenn Sie dies tun, wenn Sie ein Element anhand des Kurznamens entfernen möchten, müssen Sie es lediglich mit dem Schlüssel aus der Hashtabelle entfernen.

Wenn Sie diese SPRoleAssignments häufig laden, ist dies leider nicht zeiteffizienter. Die Vorschläge, die andere Leute zur Verwendung von Linq gemacht haben, sind gut, wenn Sie eine neue Version von .NET Framework verwenden. Andernfalls müssen Sie sich an die von Ihnen verwendete Methode halten.

0
Ed Altorfer

Speichern Sie Ihre Artikel zuerst und löschen Sie sie.

var itemsToDelete = Items.Where(x => !!!your condition!!!).ToArray();
for (int i = 0; i < itemsToDelete.Length; ++i)
    Items.Remove(itemsToDelete[i]);

Sie müssen GetHashCode() in Ihrer Item-Klasse überschreiben.

0
Stas BZ