3 Stimmen

Entfernen von Elementen aus IDictionary mit Rekursion

Hat jemand eine bessere Methode, dies zu tun? Scheint, wie es sollte einfacher sein als diese, aber ich habe eine geistige Blockade. Im Grunde muss ich Elemente aus einem Wörterbuch zu entfernen und rekursieren in die Werte der Elemente, die auch Wörterbücher sind.

private void RemoveNotPermittedItems(ActionDictionary menu)
{
    var keysToRemove = new List<string>();
    foreach (var item in menu)
    {
        if (!GetIsPermitted(item.Value.Call))
        {
            keysToRemove.Add(item.Key);
        }
        else if (item.Value is ActionDictionary)
        {
            RemoveNotPermittedItems((ActionDictionary)item.Value);
            if (((ActionDictionary)item.Value).Count == 0)
            {
                keysToRemove.Add(item.Key);
            }
        }
    }
    foreach (var key in (from item in menu where keysToRemove.Contains(item.Key) select item.Key).ToArray())
    {
        menu.Remove(key);
    }
}

Das Aktionswörterbuch sieht so aus:

public class ActionDictionary : Dictionary<string, IActionItem>, IActionItem

0 Stimmen

Was ist "glaubwürdig" und/oder "offiziell"?

3voto

Jared Punkte 1987

Wenn Sie das Wörterbuch in umgekehrter Reihenfolge durchlaufen (von 'menu.Count - 1' bis Null), brauchen Sie die Schlüssel nicht zu sammeln und erneut zu iterieren. Bei der Iteration in Vorwärtsrichtung kommt es natürlich zu Ausnahmen bei mutierten Sammlungen, wenn Sie anfangen, Dinge zu entfernen.

Da ich nicht weiß, was ein ActionDictionary ist, kann ich Ihr genaues Szenario nicht testen, aber hier ist ein Beispiel, das nur Dictionary<string,object> .

    static int counter = 0;
    private static void RemoveNotPermittedItems(Dictionary<string, object> menu)
    {
        for (int c = menu.Count - 1; c >= 0; c--)
        {
            var key = menu.Keys.ElementAt(c);
            var value = menu[key];
            if (value is Dictionary<string, object>)
            {
                RemoveNotPermittedItems((Dictionary<string, object>)value);
                if (((Dictionary<string, object>)value).Count == 0)
                {
                    menu.Remove(key);
                }
            }
            else if (!GetIsPermitted(value))
            {
                menu.Remove(key);
            }
        }
    }

    // This just added to actually cause some elements to be removed...
    private static bool GetIsPermitted(object value)
    {
        if (counter++ % 2 == 0)
            return false;
        return true;
    }

Ich habe auch die "if"-Anweisung umgekehrt, aber das war nur eine Annahme, dass Sie möchten Typüberprüfung vor dem Aufruf einer Methode auf den Wert des Elements zu handeln... es funktioniert so oder so vorausgesetzt, "GetIsPermitted" gibt immer TRUE für ActionDictionary.

Ich hoffe, das hilft.

0 Stimmen

Das ist richtig. Die Ausnahme ist, dass ich das IF nicht umkehren sollte, weil es ein anderes Verhalten hervorrufen würde. Wenn ein übergeordnetes Element nicht zulässig ist, möchte ich es entfernen, auch wenn es zulässige Kinder hat.

0 Stimmen

@TimScott Warum sollten Sie rekursiv löschen, wenn wir den übergeordneten Knoten löschen können, ohne die Kinder zu löschen.

2voto

Jon Skeet Punkte 1325502

Zunächst wird Ihr foreach Schleife ist viel komplizierter, als es sein müsste. Tun Sie es einfach:

foreach (var key in keysToRemove)
{
    menu.Remove(key);
}

Ich bin etwas überrascht, dass Dictionary hat keine RemoveAll Methode, aber es sieht nicht so aus, als ob es das tut...

1 Stimmen

Das funktioniert nicht. Sie können ein Element nicht entfernen, während Sie durch eine Sammlung iterieren. Sie werden eine Ausnahme erhalten, dass die Sammlung geändert wurde.

1 Stimmen

Das funktioniert allerdings nicht mit dem ursprünglichen Array, so dass das kein Problem darstellt. Er spricht über die zweite foreach, nicht die erste.

0 Stimmen

Verstanden, natürlich, siehe meine vorherige Bemerkung über die "mentale Blockade" :) Trotzdem würde ich gerne glauben, dass es in einer Schleife erledigt werden kann.

2voto

Lex Li Punkte 56075

Während foreach und GetEnumerator fehlschlagen, funktioniert eine for-Schleife,

var table = new Dictionary<string, int>() {{"first", 1}, {"second", 2}}; for (int i = 0; i < table.Keys.Count; i++)//string key in table.Keys) { string key = table.Keys.ElementAt(i); if (key.StartsWith("f")) { table.Remove(key); } }

Aber ElementAt() ist eine .NET 3.5 Funktion.

1voto

Geoff Cox Punkte 6064

Option 1: Ein Wörterbuch ist immer noch eine Sammlung. Iterieren Sie über menu.Values.

Sie können über menu.Values iterieren und sie bei der Iteration entfernen. Die Werte werden nicht in einer sortierten Reihenfolge angezeigt (was für Ihren Fall in Ordnung sein sollte). Möglicherweise müssen Sie eine for-Schleife verwenden und den Index anpassen, anstatt foreach zu verwenden - der Enumerator löst eine Ausnahme aus, wenn Sie die Sammlung während der Iteration ändern.

(Ich werde versuchen, den Code hinzuzufügen, wenn ich an meinem Entwicklungsrechner bin, Mo)

Option 2: Erstellen Sie einen eigenen Iterator.

Einige Sammlungen, die von ListBox SelectedItems in Winforms zurückgegeben werden, enthalten nicht wirklich die Sammlung, sie bieten eine Hülle um die zugrunde liegende Sammlung. So ähnlich wie CollectionViewSource in WPF. ReadOnlyCollection tut etwas ähnliches zu.

Erstellen Sie eine Klasse, die Ihre verschachtelten Dictionaries in etwas "flatten" kann, das sie wie eine einzige Sammlung aufzählen kann. Implementieren Sie eine Löschfunktion, die so aussieht, als ob sie ein Element aus der Sammlung entfernt, aber tatsächlich aus dem aktuellen Wörterbuch entfernt.

1voto

Writwick Punkte 2085

In My Opinion können Sie Ihre eigene generische Klasse definieren, die von der KeyValuePair<...> werden sowohl TKey als auch TValue List<T> und Sie können die RemoveAll oder die RemoveRange der List<T> in einer neuen RemoveRange() o RemoveAll() Methode in Ihrer abgeleiteten Klasse, um die gewünschten Elemente zu entfernen.

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