【问题标题】:Removing Items From IDictionary With Recursion使用递归从 IDictionary 中删除项目
【发布时间】:2010-09-19 02:59:42
【问题描述】:

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

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

【问题讨论】:

  • 什么是“可信”和/或“官方”?

标签: c# recursion idictionary


【解决方案1】:

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

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

    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。

希望这会有所帮助。

【讨论】:

  • 这是正确的。例外是我不应该反转 IF,因为它会产生不同的行为。如果不允许父项,即使它允许子项,我也想将其删除。
  • @TimScott 为什么要递归删除,如果我们可以删除父节点而不删除子节点。
【解决方案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 的功能。

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 这不起作用。迭代集合时不能删除项目。您将收到集合已被修改的异常。
    • 这不是在原始阵列上运行的,所以这不是问题。他说的是第二个 foreach,而不是第一个。
    • 知道了,当然,参考我之前关于“心理障碍”的评论。 :) 我还是想认为它可以在一个循环中完成。
    • 好吧,你可以有一个“选择”,它只允许通过适当的条目 - 然后在最后调用 ToDictionary。不过会很丑。
    • @JesseC.Slicer:有副作用,否则毫无意义。它不是在改变 list,而是在改变字典。
    【解决方案4】:

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

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

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

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

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

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

    【讨论】:

      【解决方案5】:

      在我看来,您可以定义自己的泛型类,该类派生自 KeyValuePair&lt;...&gt;,TKey 和 TValue 都将是 List&lt;T&gt;,您可以在新的 List&lt;T&gt; 中使用 RemoveAllRemoveRange派生类中的RemoveRange()RemoveAll() 方法来删​​除您想要的项目。

      【讨论】:

        【解决方案6】:

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

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

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

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

            keysToRemove 的类型更改为HashSet&lt;string&gt;,你会得到一个O(1) Contains 方法。对于List&lt;string&gt;,它是 O(n),正如您所猜测的那样慢。

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                【解决方案10】:

                我认为

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

                应该工作得很快

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-03-04
                  • 2023-04-03
                  • 1970-01-01
                  • 2020-04-18
                  • 1970-01-01
                  • 2011-02-11
                  • 1970-01-01
                  相关资源
                  最近更新 更多