Je dois supprimer plusieurs éléments d'un dictionnaire. Un moyen simple de le faire est le suivant:
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);
La raison pour laquelle je ne peux pas supprimer directement les éléments du bloc foreach est que cela déclencherait une exception ("La collection a été modifiée ...")
Je voudrais faire ce qui suit:
MyCollection.RemoveAll(x =>x.Member==foo)
Mais la classe Dictionary <> n'expose pas une méthode RemoveAll (Predicate <> Match), comme le fait la classe List <>.
Quelle est la meilleure façon (à la fois en termes de performances et d'élégance) de le faire?
Voici une autre façon
foreach ( var s in MyCollection.Where(kv => kv.Value.Member == foo).ToList() ) {
MyCollection.Remove(s.Key);
}
Pousser le code directement dans une liste vous permet d'éviter le problème de "suppression lors de l'énumération". La .ToList()
forcera l'énumération avant que foreach ne démarre vraiment.
vous pouvez créer un méthode d'extension :
public static class DictionaryExtensions
{
public static void RemoveAll<TKey, TValue>(this IDictionary<TKey, TValue> dict,
Func<TValue, bool> predicate)
{
var keys = dict.Keys.Where(k => predicate(dict[k])).ToList();
foreach (var key in keys)
{
dict.Remove(key);
}
}
}
...
dictionary.RemoveAll(x => x.Member == foo);
Au lieu de supprimer, faites simplement l'inverse. Créez un nouveau dictionnaire à partir de l'ancien contenant uniquement les éléments qui vous intéressent.
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);
}
Version modifiée de la solution de méthode d'extension d'Aku. La principale différence est qu'elle permet au prédicat d'utiliser la clé du dictionnaire. Une différence mineure est qu'elle étend IDictionary plutôt que 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);
Pouvez-vous simplement changer votre boucle pour utiliser un index (c'est-à-dire FOR au lieu de FOREACH)? Il faudrait bien sûr boucler en arrière, c'est-à-dire compter-1 jusqu'à zéro.
Au lieu de supprimer, faites simplement l'inverse (créez un nouveau dictionnaire à partir de l'ancien contenant uniquement les éléments qui vous intéressent) et laissez le garbage collector s'occuper de l'ancien dictionnaire:
var newDictionary = oldDictionary.Where(x => x.Value != foo);