Frage

Wer ein intelligenter Weg, dies zu tun?Scheint, wie es sollte einfacher sein als das, aber ich habe eine mentale Blockade.Im Prinzip brauche ich zum entfernen von Elementen aus einem Wörterbuch und recurse-in die Werte der Elemente, die sind auch Wörterbücher.

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

Aktion Wörterbuch ist wie folgt:

public class ActionDictionary : Dictionary<string, IActionItem>, IActionItem
War es hilfreich?

Lösung

Sie brauchen nicht wirklich die Schlüssel zu sammeln und wiederholen sie wieder, wenn Sie das Wörterbuch in umgekehrter Reihenfolge durchlaufen (von ‚menu.Count - 1‘ auf Null). Iterieren in Vorwärtsrichtung wird natürlich, Ausbeute Sammlung Ausnahmen mutiert, wenn Sie entfernen die Dinge beginnen.

Ich weiß nicht, was ein ActionDictionary ist, so konnte ich Ihre genaue Szenario nicht testen, aber hier ist ein Beispiel nur Dictionary<string,object> verwendet wird.

    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 umgekehrt auch die ‚if‘ Anweisung, aber das war nur eine Vermutung, dass Sie Typprüfung wollen würden tun, bevor der Aufruf eine Methode auf dem Produktwert zu handeln ... es wird funktionieren oder so ‚GetIsPermitted‘ immer vorausgesetzt, liefert TRUE für ActionDictionary.

Hope, das hilft.

Andere Tipps

Während foreach und GetEnumerator ausfällt, ein for-Schleife funktioniert,

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.

Mit zu beginnen, Ihre foreach Schleife ist viel komplizierter, als es sein muss. Genau das tun:

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

Ich bin ein wenig überrascht, dass Dictionary keine RemoveAll Methode hat, aber es sieht nicht so aus, wie es funktioniert ...

Option 1:Ein Wörterbuch ist immer noch eine Sammlung.Durchlaufen-Menü.Werte.

Sie iterieren über Menü.Werte und entfernen Sie Sie, wie Sie iterieren.Die Werte werden nicht in einer sortierten Reihenfolge (was in Ordnung sein sollte, für Ihren Fall).Möglicherweise müssen Sie eine for-Schleife verwenden und anpassen der index eher als unter Verwendung der foreach - enumerator eine Ausnahme werfen, wenn Sie ändern die Sammlung während der Iteration.

(Ich werde versuchen, fügen Sie den code, wenn ich auf meiner dev-Maschine, Mo)

Option 2:Erstellen Sie einen benutzerdefinierten iterator.

Einige Sammlungen zurückgegeben ListBox SelectedItems in Winforms nicht wirklich enthalten die Kollektion, bieten Sie einen wrapper für die zugrunde liegenden Auflistung.Eine Art, wie CollectionViewSource in WPF.ReadOnlyCollection etwas ähnliches ist es auch.

Eine Klasse erstellen, können Sie "abflachen" der verschachtelte Wörterbücher in etwas, das auflisten können, die über Ihnen wie Sie aus einer einzigen Sammlung.Implementieren Sie eine Funktion löschen, wie es aussieht, entfernt ein Element aus der Kollektion, sondern wirklich entfernt aus dem aktuellen Wörterbuch.

Meiner Meinung nach, können Sie Ihren eigenen generische Klasse im Zusammenhang mit dem KeyValuePair<...> definieren sowohl TKey und TValue wird List<T> werden, und Sie können die RemoveAll oder die RemoveRange des List<T> in einer neuen RemoveRange() oder RemoveAll() Methode in der abgeleiteten Klasse verwenden, um zu entfernen Artikel, die Sie wollen.

Ich weiß, dass Sie wahrscheinlich eine gute Lösung bereits gefunden, aber nur für den Grund der ‚Schickeria‘, wenn Sie Ihre Methode Signaturen ändern könnte (ich weiß es nicht in Ihrem Szenario geeignet sein können):

private ActionDictionary RemoveNotPermittedItems(ActionDictionary menu)
{
 return new ActionDictionary(from item in menu where GetIsPermitted(item.Value.Call) select item)
.ToDictionary(d=>d.Key, d=>d.Value is ActionDictionary?RemoveNotPermittedItems(d.Value as ActionDictionary) : d.Value));
}

Und ich kann paar Möglichkeiten sehen, wo Sie Ihr Wörterbuch mit gefilterter Artikel ohne Modifikation und materialisieren neue Wörterbücher verwenden können.

Es ist nicht viel weniger kompliziert, aber einige idiomatische Änderungen machen es ein bisschen kürzer und leichter auf die Augen:

    private static void RemoveNotPermittedItems(IDictionary<string, IActionItem> menu)
    {
        var keysToRemove = new List<string>();

        foreach (var item in menu)
        {
            if (GetIsPermitted(item.Value.Call))
            {
                var value = item.Value as ActionDictionary;

                if (value != null)
                {
                    RemoveNotPermittedItems(value);
                    if (!value.Any())
                    {
                        keysToRemove.Add(item.Key);
                    }
                }
            }
            else
            {
                keysToRemove.Add(item.Key);
            }
        }

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

    private static bool GetIsPermitted(object call)
    {
        return ...;
    }

Ändern Sie den Typ des keysToRemove zu HashSet<string> und Sie erhalten eine O (1) Contains Verfahren erhalten. Mit List<string> ist es O (n), die langsamer ist wie Sie sich vorstellen können.

nicht getestet, bis ich morgen an meinem VS Maschine bin: o

private void RemoveNotPermittedItems(ActionDictionary menu)
{
    foreach(var _checked in (from m in menu
                             select new
                             {
                                 gip = !GetIsPermitted(m.Value.Call),
                                 recur = m.Value is ActionDictionary,
                                 item = m
                             }).ToArray())
    {
        ActionDictionary tmp = _checked.item.Value as ActionDictionary;
        if (_checked.recur)
        {
            RemoveNotPermittedItems(tmp);
        }
        if (_checked.gip || (tmp != null && tmp.Count == 0) {
            menu.Remove(_checked.item.Key);
        }
    }
}

Ich denke

public class ActionSet : HashSet<IActionItem>, IActionItem

Und

bool Clean(ActionSet nodes)
    {
        if (nodes != null)
        {
            var removed = nodes.Where(n => this.IsNullOrNotPermitted(n) || !this.IsNotSetOrNotEmpty(n) || !this.Clean(n as ActionSet));

            removed.ToList().ForEach(n => nodes.Remove(n));

            return nodes.Any();
        }

        return true;
    }

    bool IsNullOrNotPermitted(IActionItem node)
    {
        return node == null || *YourTest*(node.Call);
    }

    bool IsNotSetOrNotEmpty(IActionItem node)
    {
        var hset = node as ActionSet;
        return hset == null || hset.Any();
    }

Sollte schnell arbeiten

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top