Лучший способ удалить несколько элементов, соответствующих предикату из словаря С#?
Мне нужно удалить несколько элементов из Словаря.
Простой способ сделать это:
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);
Причина, по которой я не могу напрямую удалить элементы в блоке foreach, заключается в том, что это вызовет исключение ( "Collection был изменен..." )
Я хотел бы сделать следующее:
MyCollection.RemoveAll(x =>x.Member==foo)
Но класс Dictionary < > не предоставляет метод RemoveAll (Predicate < > Match), как это делает List < > Class.
Какой лучший способ (как умный, так и элегантный) сделать это?
Ответы
Ответ 1
Здесь альтернативный способ
foreach ( var s in MyCollection.Where(kv => kv.Value.Member == foo).ToList() ) {
MyCollection.Remove(s.Key);
}
Нажатие кода в список напрямую позволяет избежать "удаления при перечислимости". Элемент .ToList()
заставит перечислить до начала запуска foreach.
Ответ 2
вы можете создать метод расширения:
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);
Ответ 3
Вместо удаления просто выполните обратный. Создайте новый словарь из старого, содержащий только те элементы, которые вас интересуют.
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);
}
Ответ 4
Модифицированная версия решения для расширения Aku. Основное различие заключается в том, что он позволяет предикату использовать ключ словаря. Небольшое различие заключается в том, что он расширяет IDictionary, а не 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);
Ответ 5
Можете ли вы просто изменить свой цикл, чтобы использовать индекс (т.е. FOR вместо FOREACH)? Вы должны были бы отступить назад, конечно, то есть счет-1 до нуля.
Ответ 6
Вместо удаления просто выполните обратный (создайте новый словарь из старого, который содержит только те элементы, которые вас интересуют), и пусть сборщик мусора позаботится о старом словаре:
var newDictionary = oldDictionary.Where(x => x.Value != foo);