使用递归从 IDictionary 中删除项目

发布于 2024-07-07 12:04:55 字数 908 浏览 14 评论 0原文

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

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

Anybody have a slicker way to do this? Seems like it should be easier than this, but I'm having a mental block. Basically I need to remove items from an dictionary and recurse into the values of the items that are also dictionaries.

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

Action dictionary is like this:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

茶底世界 2024-07-14 12:04:55

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

我不知道 ActionDictionary 是什么,所以我无法测试您的确切场景,但这里有一个仅使用 Dictionary 的示例。

    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”语句,但这只是一个假设,即您希望在调用对项目的值进行操作的方法之前进行类型检查...假设“GetIsPermission”始终返回 TRUE,那么无论哪种方式都可以工作行动词典。

希望这可以帮助。

You don't really need to collect the keys and iterate them again if you iterate the dictionary in reverse (from 'menu.Count - 1' to zero). Iterating in forward order will, of course, yield mutated collection exceptions if you start removing things.

I don't know what an ActionDictionary is, so I couldn't test your exact scenario, but here's an example using 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;
    }

I also reversed the 'if' statement, but that was just an assumption that you'd want to do type checking before calling a method to act on the item's value...it will work either way assuming 'GetIsPermitted' always returns TRUE for ActionDictionary.

Hope this helps.

动听の歌 2024-07-14 12:04:55

虽然 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 的一项功能。

While foreach and GetEnumerator fails, a for-loop works,

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

But ElementAt() is a .NET 3.5 feature.

南巷近海 2024-07-14 12:04:55

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

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

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

To start with, your foreach loop is way more complicated than it needs to be. Just do:

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

I'm slightly surprised that Dictionary doesn't have a RemoveAll method but it doesn't look like it does...

蓦然回首 2024-07-14 12:04:55

选项 1:字典仍然是集合。 迭代 menu.Values。

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

(周一,我将在我的开发机器上尝试添加代码)

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

从 Winforms 中的 ListBox SelectedItems 返回的一些集合并不真正包含该集合,它们提供了底层集合的包装。 有点像 WPF 中的 CollectionViewSource。 ReadOnlyCollection 也做了类似的事情。

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

Option 1: A Dictionary is still a Collection. Iterate over menu.Values.

You can iterate over menu.Values and remove them as you iterate. The values won't come in any sorted order (which should be fine for your case). You may need to use a for loop and adjust the index rather than using foreach - the enumerator will throw an exception if you modify the collection while iterating.

(I'll try to add the code when I'm on my dev machine Mon)

Option 2: Create a custom iterator.

Some collections returned from ListBox SelectedItems in Winforms don't really contain the collection, they provide a wrapper around the underlying collection. Kind of like CollectionViewSource in WPF. ReadOnlyCollection does something similar too.

Create a class that can "flatten" your nested dictionaries into something that can enumerate over them like they are a single collection. Implement a delete function that looks like it removes an item from the collection, but really removes from the current dictionary.

千纸鹤带着心事 2024-07-14 12:04:55

在我的观点中,您可以定义自己的从 KeyValuePair<...> 派生的泛型类,TKey 和 TValue 都将是 List 并且您可以使用新的 RemoveRange()RemoveAll( 中 ListRemoveAllRemoveRange ) 方法在您的派生类中删除您想要的项目。

In My Opinion, you can define your own generic class deriving from the KeyValuePair<...> both TKey and TValue will be List<T> and you can use the RemoveAll or the RemoveRange of the List<T> in a new RemoveRange() or RemoveAll() method in your derived class to Remove the items you want.

許願樹丅啲祈禱 2024-07-14 12:04:55

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

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

而且我可以看到几种方法,您可以使用您的方法带有过滤项目的字典,无需修改并具体化新字典。

I know you probably found good solution already, but just for the reason of 'slickness' if you could modify your method signatures to (I know it may not be appropriate in your scenario):

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

And I can see couple ways where you can use your dictionary with filtered items without modification and materializing new dictionaries.

夜清冷一曲。 2024-07-14 12:04:55

它并没有那么复杂,但是一些惯用的改变使它变得更短并且更容易看:

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

It's not much less complicated, but some idiomatic changes make it a bit shorter and easier on the eyes:

    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 ...;
    }
回忆凄美了谁 2024-07-14 12:04:55

keysToRemove 的类型更改为 HashSet,您将获得 O(1) Contains 方法。 对于 List ,它的复杂度为 O(n),正如您可能猜到的那样,速度较慢。

Change the type of the keysToRemove to HashSet<string> and you'll get an O(1) Contains method. With List<string> it's O(n), which is slower as you might guess.

随梦而飞# 2024-07-14 12:04:55

明天我在我的 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);
        }
    }
}

untested until i'm at my VS machine tomorrow :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);
        }
    }
}
昨迟人 2024-07-14 12:04:55

我认为

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

应该工作得快

I think

public class ActionSet : HashSet<IActionItem>, IActionItem

And

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

Should work fast

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文