使用递归从IDictionary中删除项目

时间:2008-10-24 22:16:30

标签: c# recursion idictionary

有人有一个更流畅的方式吗?看起来它应该比这更容易,但我有心理障碍。基本上我需要从字典中删除项目并递归到也是字典的项目的值。

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

动作词典是这样的:

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

10 个答案:

答案 0 :(得分:3)

如果你反向迭代字典(从'menu.Count - 1'到零),你真的不需要收集密钥并再次迭代它们。当然,如果你开始删除事物,那么按顺序迭代将产生变异的集合异常。

我不知道ActionDictionary是什么,所以我无法测试你的确切场景,但这是一个仅使用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;
    }

我还颠倒了'if'语句,但这只是假设你想在调用一个方法来对项目的值进行操作之前进行类型检查......它将以任何方式工作,假设'GetIsPermitted'总是对ActionDictionary返回TRUE。

希望这有帮助。

答案 1 :(得分:2)

首先,您的foreach循环比它需要的更复杂。只是做:

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

我有点惊讶Dictionary没有RemoveAll方法,但它看起来不像......

答案 2 :(得分:2)

当foreach和GetEnumerator失败时,for循环工作,

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

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

但ElementAt()是.NET 3.5的一项功能。

答案 3 :(得分:1)

选项1:字典仍然是一个集合。迭代菜单。值。

您可以遍历menu.Values并在迭代时删除它们。这些值不会以任何排序顺序排列(对于您的情况应该没问题)。您可能需要使用for循环并调整索引而不是使用foreach - 如果在迭代时修改集合,枚举器将抛出异常。

(当我在我的开发机器Mon上时,我会尝试添加代码)

选项2:创建自定义迭代器。

从Winforms中的ListBox SelectedItems返回的某些集合实际上并不包含集合,它们提供了基础集合的包装。有点像WPF中的CollectionViewSource。 ReadOnlyCollection也做了类似的事情。

创建一个类,可以将嵌套的词典“展平”为可以枚举它们的东西,就像它们是单个集合一样。实现一个删除函数,它看起来像是从集合中删除了一个项目,但实际上是从当前字典中删除的。

答案 4 :(得分:1)

在我的意见中,您可以定义自己的通用类派生自KeyValuePair<...> TKey和TValue将List<T>,您可以使用RemoveAllRemoveRange派生类中新List<T>RemoveRange()方法中的RemoveAll(),以删除所需的项目。

答案 5 :(得分:1)

我知道你可能已经找到了很好的解决方案,但只是出于“光滑”的原因,如果你可以修改你的方法签名(我知道它可能不适合你的场景):

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

我可以通过几种方式看到您可以将字典与过滤后的项目一起使用而无需修改并实现新词典。

答案 6 :(得分:1)

它并没有那么简单,但是一些惯用的改变使它更短更容易在眼睛上看:

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

答案 7 :(得分:1)

keysToRemove的类型更改为HashSet<string>,您将获得O(1)Contains方法。使用List<string>它是O(n),这可能比你想象的要慢。

答案 8 :(得分:1)

未经测试直到我明天在我的VS机器上: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);
        }
    }
}

答案 9 :(得分:1)

我认为

public class ActionSet : HashSet<IActionItem>, IActionItem

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

应该快速工作