如何使用lambda表达式从字典中删除项目?

46
我正在使用简单的谓词来确定是否应该删除键。 例如,如果字典是这样构建的Dictionary<int, int>,如何删除所有具有负值的条目?
我更喜欢使用同一个字典,而不是创建新的字典。
我没有性能问题。
有没有一种方法可以做到这一点,而不使用LINQ,而是使用Lambda表达式?
我不想在LINQ中寻找解决方案,因为我的项目中没有人使用它们,也不想成为第一个使用它们的人。但是因为我发现LINQ的解决方案看起来更好,所以我会使用它们。

键或值中有负数据?还是两者都有? - MonkeyDeveloper
1
我认为在这种情况下,你不能使用lambda表达式而不使用linq。 - Ahmed Magdy
8个回答

98

如果您愿意的话,最简单的方法可能是创建一个字典:

var newDictionary = oldDictionary.Where(pair => pair.Value >= 0)
                                 .ToDictionary(pair => pair.Key,
                                               pair => pair.Value);

如果你必须改变现有的字典(例如因为其他几个对象引用了同一个字典),则需要构建一个要删除的键列表,然后在之后将它们删除:

var toRemove = dictionary.Where(pair => pair.Value < 0)
                         .Select(pair => pair.Key)
                         .ToList();

foreach (var key in toRemove)
{
    dictionary.Remove(key);
}

编辑:我刚刚注意到第一句话:“我不喜欢使用LINQ解决方案”。如果这意味着您不想使用LINQ解决方案,则以下是手动版本:

List<int> toRemove = new List<int>();
foreach (KeyValuePair<int, int> pair in dictionary)
{
    if (pair.Value < 0)
    {
        toRemove.Add(pair.Key);
    }
}

foreach (var key in toRemove)
{
    dictionary.Remove(key);
}

......但是如果你能够使用LINQ,我鼓励你使用。我第二个解决方案与“手动”版本等效,但在我看来更易读。


我应该添加哪个引用来支持“Where”关键字? - Delashmate
1
@Delashmate:添加对System.Core的引用并添加using System.Linq;。这是假设您正在使用.NET 3.5或更高版本。 - Jon Skeet
@Delashmate:我刚看到你说“不喜欢LINQ解决方案”的那一部分 - 你是指你没有使用过它们,还是你不想要一个LINQ解决方案? - Jon Skeet
13
如果你们项目中每个人都持有这种态度,那你们将永远不会使用LINQ。难道这里的LINQ解决方案不是更简单易懂、更具声明性吗?我强烈建议你采用LINQ——它是一项绝妙的技术。至少要与你的团队成员讨论一下。 - Jon Skeet
1
@Jon,我接受了你的建议 :) - Delashmate
显示剩余2条评论

13

仅通过使用lambda表达式:

foreach (var i in myDict.Where(d => (d.Value  < 0 || d.key <0)).ToList() ) 
{
  myDict.Remove(i.Key);
}

你不觉得这样会出问题吗?因为你在迭代集合的同时修改了它。 - Svend Hansen
3
ToList()方法会创建一个克隆,因此您正在迭代克隆并从原始字典中删除。 - Kamyar
哦耶!我猜没有 ToList() 它会出错 :) - Svend Hansen

5
var toRemove = dict.Keys.Where(predicate).ToArray();
foreach (var key in toRemove) {
    dict.Remove(key);
}

1
请注意,ToList通常比ToArray更高效。除非你需要一个数组,否则我认为ToList更可取。 - Jon Skeet
@Jon,不是我怀疑你,但为什么会这样呢?难道数组不比列表更轻量级吗? - Michael Low
7
一个数组必须是恰好的合适大小。通常构建 "大" 集合的方式是每次需要时将缓冲区大小加倍。这对于列表很有效,因为您可以随后使用过大的缓冲区 - 但是如果您之后需要一个数组,您通常需要将缓冲区中的所有数据复制到一个恰好合适大小的 数组中。请参阅http://msmvps.com/blogs/jon_skeet/archive/2011/01/02/reimplementing-linq-to-objects-part-24-toarray.aspx 看看 ToArray 大致上要做什么。 - Jon Skeet

1
从.Net Core 3.0开始,在枚举过程中删除字典项是安全的。因此,最简单的解决方案是:
foreach (var kv in dictionary)
    if (kv.Value < 0)
        dictionary.Remove(kv.Key);

查看Dictionary<TKey,TValue>.Remove方法的文档中的备注:

.NET Core 3.0+ only:此变异方法可以在不使活动枚举器失效的情况下安全调用Dictionary<TKey,TValue>实例。这并不意味着线程安全。

而这就是这个功能成为现实的方式:允许在枚举期间删除Dictionary<K,V>项

0
你想从字典中删除这些项目,还是愿意使用一个不包含这些项目的新字典?
var d = new Dictionary<int,int>();
var newDict = d.Where(entry => entry.Value >= 0).ToDictionary(entry => entry.Key, entry => entry.Value);

0

最简单的一个:

Dictionary<long, long> dict...
Dictionary<long, long> result = dict.Were(x => x.Value >= 0).ToDictionary(x => x.Key, x => x.Value);

或者只需以相反的顺序在“for”中循环并删除无效的内容。


0

如果你添加

namespace MMExtensions
{
    public static class DictionaryExtensions
    {
        public delegate bool Predicate<TKey, TValue>(KeyValuePair<TKey, TValue> d);

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void Filter<TKey, TValue>(
            this Dictionary<TKey, TValue> hashtable, Predicate<TKey, TValue> p)
        {
            foreach (KeyValuePair<TKey, TValue> value in hashtable.ToList().Where(value => !p(value)))
                hashtable.Remove(value.Key);
        }
    }
}

假设你有一个数据集,它是以字典的形式存在的:

    Dictionary<string, int> d =
            new Dictionary<string, int> {{"v", -3}, {"val1", 1}, {"val2", 2}};

然后你可以使用:

    d.Filter(delegate(KeyValuePair<string, int> kv) { return kv.Value >= 0; });
    d.Filter(kv => kv.Value >= 0);// or as lambda

我认为你不需要在这里使用 MethodImplOptions.Synchronized - RobSiklos

0

我知道你说你不喜欢Linq,但是我无法抑制自己提供以下解决方案,而且如果你读一下你问题的标题,它仍然很有用。这可能是你问题最优雅的解决方案:

dictionary.Where(pair => pair.Value < 0)
          .Select(pair => { 
              dictionary.Remove(pair.Key);
              return pair.Key;
          });

使用Any代替Select,并配合return false(或者更自然的Allreturn true - Jiří

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接