Best way to remove multiple items matching a predicate from a C # dictionary?

I need to remove several items from the Dictionary. A simple way to do this:

List<string> keystoremove= new List<string>(); foreach (KeyValuePair<string,object> k in MyCollection) if (k.Value.Member==foo) keystoremove.Add(k.Key); foreach (string s in keystoremove) MyCollection.Remove(s); 

The reason I cannot directly delete elements in the foreach block is because it will throw an exception ("Collection has been changed ...")

I would like to do the following:

  MyCollection.RemoveAll(x =>x.Member==foo) 

But the Dictionary <> class does not provide the RemoveAll (Predicate <> Match) method, as does the List <> Class.

What is the best way (smart as well as elegant) to do this?

+49
collections dictionary c # linq
Jan 22 '09 at 13:57
source share
6 answers

Here is an alternative way

 foreach ( var s in MyCollection.Where(kv => kv.Value.Member == foo).ToList() ) { MyCollection.Remove(s.Key); } 

Pressing the code into the list directly avoids the "delete at enumeration". The .ToList() element will enumerate before starting foreach.

+77
Jan 22 '09 at 14:06
source share

you can create an extension method :

 public static class DictionaryExtensions { public static void RemoveAll<TKey, TValue>(this Dictionary<TKey, TValue> dic, Func<TValue, bool> predicate) { var keys = dic.Keys.Where(k => predicate(dic[k])).ToList(); foreach (var key in keys) { dic.Remove(key); } } } ... dictionary.RemoveAll(x => x.Member == foo); 
+19
Jan 22 '09 at 14:15
source share

Instead of deleting, just do the opposite. Create a new dictionary from the old one containing only those items that interest you.

 public Dictionary<T, U> NewDictionaryFiltered<T, U> ( Dictionary<T, U> source, Func<T, U, bool> filter ) { return source .Where(x => filter(x.Key, x.Value)) .ToDictionary(x => x.Key, x => x.Value); } 
+10
Jan 22 '09 at 15:08
source share

A modified version of the Aku extension solution. The main difference is that it allows the predicate to use the dictionary key. The slight difference is that it extends IDictionary, not Dictionary.

 public static class DictionaryExtensions { public static void RemoveAll<TKey, TValue>(this IDictionary<TKey, TValue> dic, Func<TKey, TValue, bool> predicate) { var keys = dic.Keys.Where(k => predicate(k, dic[k])).ToList(); foreach (var key in keys) { dic.Remove(key); } } } . . . dictionary.RemoveAll((k,v) => v.Member == foo); 
+10
Aug 22 2018-11-22T00:
source share

Can you just change your loop to use an index (i.e. FOR instead of FOREACH)? You would have to step back, of course, that is, count-1 to zero.

0
Jan 22 '09 at 14:06
source share

Instead of deleting, just do the opposite (create a new dictionary from the old one that contains only those items that interest you), and let the garbage collector take care of the old dictionary:

 var newDictionary = oldDictionary.Where(x => x.Value != foo); 
-one
Jan 22 '09 at 14:07
source share



All Articles