3

有人有更巧妙的方法来做到这一点吗?似乎它应该比这更容易,但我有一个心理障碍。基本上我需要从字典中删除项目并递归到也是字典的项目的值。

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
4

10 回答 10

3

如果您反向迭代字典(从“menu.Count - 1”到零),您实际上不需要收集键并再次迭代它们。当然,如果您开始删除事物,则按正序迭代会产生变异的集合异常。

我不知道 ActionDictionary 是什么,所以我无法测试您的确切场景,但这里有一个使用 just 的示例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”总是返回TRUE动作字典。

希望这可以帮助。

于 2008-10-25T06:17:26.563 回答
2

首先,您的foreach循环比它需要的要复杂得多。做就是了:

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

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

于 2008-10-24T22:24:23.070 回答
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);
    }
}

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

于 2008-10-25T04:44:43.693 回答
1

选项 1:字典仍然是一个集合。遍历 menu.Values。

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

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

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

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

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

于 2008-10-25T01:43:58.253 回答
1

在 My Opinion 中,您可以定义自己的泛型类,该类派生自KeyValuePair<...>TKey 和 TValue 将是List<T>,并且您可以在派生类的新方法或方法中使用 theRemoveAllRemoveRangeof来删除所需的项目。List<T>RemoveRange()RemoveAll()

于 2012-05-12T17:45:36.180 回答
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));
}

我可以看到几种方法,您可以在不修改和具体化新词典的情况下使用带有过滤项目的词典。

于 2012-05-16T16:53:58.533 回答
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 ...;
    }
于 2012-05-17T15:41:57.687 回答
1

更改keysToRemoveto的类型,HashSet<string>您将获得 O(1)Contains方法。使用List<string>它是 O(n),您可能会猜到它会更慢。

于 2012-05-17T17:22:37.557 回答
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);
        }
    }
}
于 2012-05-18T03:06:06.170 回答
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();
    }

应该快速工作

于 2012-05-18T10:15:34.667 回答