С# Слияние 2 словарей
Я разрабатываю приложение в С#, ориентированном на .NET 3.5. В нем у меня есть 2 похожих словаря, которые содержат критерии проверки для определенного набора элементов в моем приложении. Оба словаря имеют одинаковые подписи. Первый словарь имеет настройки по умолчанию, а второй словарь содержит некоторые пользовательские настройки.
var default_settings = new Dictionary<string, MyElementSettings>();
var custom_settings = new Dictionary<string, MyElementSettings>();
Я хотел бы объединить 2 словаря в один, содержащий элементы обоих словарей.
Проблема, с которой я сталкиваюсь, - это то, что оба словаря могут иметь одни и те же ключевые значения. Основное правило, которое я хочу, состоит в том, чтобы иметь комбинацию словаря, и если в настройках custom_settings, которые уже существуют в параметрах default_settings, есть какие-либо ключи, значение custom_settings перезапишет значение default_settings. Лучшее решение у меня есть только цикл foreach, проверьте, существует ли ключ в другом словаре, а если нет, добавьте его.
foreach (var item in custom_settings)
{
if (default_settings.ContainsKey(item.Key))
default_settings[item.Key] = item.Value;
else
default_settings.Add(item.Key, item.Value);
}
Я сделал некоторые основные запросы LINQ, но я все еще работаю над тем, чтобы изучить более продвинутые вещи. Я видел несколько запросов, которые объединит 2 словаря, но большинство из них связано с группировкой любого элемента с дублирующимися ключами или возвращением коллекции только с дублирующимися ключами/есть ли запрос или выражение LINQ, которые будут имитировать поведение цикла foreach Я использую?
Ответы
Ответ 1
Две точки:
- LINQ не подходит для выполнения побочных эффектов. В этом случае вы пытаетесь мутировать существующую коллекцию, а не выполнять запрос, поэтому я бы уклонился от чистого решения LINQ.
- setter в универсальном индексаторе словарей уже имеет эффект добавления пары ключ-значение, если ключ не существует, или переписывание значение, если это произойдет.
Когда вы устанавливаете значение свойства, если ключ находится в словаре, значение, связанное с этот ключ заменяется назначенным стоимость. Если ключ не находится в Словарь, ключ и значение добавляются в словарь.
Итак, ваш цикл foreach
по существу эквивалентен:
foreach (var item in custom_settings)
{
default_settings[item.Key] = item.Value;
}
Теперь, когда это довольно красное, поэтому я не думаю, что LINQ поможет вам в этом.
Ответ 2
Вот хороший метод расширения, основанный на ответе Ани.
public static class DictionaryExtensionMethods
{
public static void Merge<TKey, TValue>(this Dictionary<TKey, TValue> me, Dictionary<TKey, TValue> merge)
{
foreach (var item in merge)
{
me[item.Key] = item.Value;
}
}
}
Ответ 3
Если вы собираетесь сделать это много, я бы рекомендовал написать сопоставитель равенства для клавиш словаря:
private class KeyEqualityComparer<T, U> : IEqualityComparer<KeyValuePair<T, U>>
{
public bool Equals(KeyValuePair<T, U> x, KeyValuePair<T, U> y)
{
return x.Key.Equals(y.Key);
}
public int GetHashCode(KeyValuePair<T, U> obj)
{
return obj.Key.GetHashCode();
}
}
И тогда, когда вам нужно объединить словари, вы можете сделать следующее
var comparer = new KeyEqualityComparer<string, MyElementSettings>();
dict1 = dict1.Union(dict2,comparer).ToDictionary(a => a.Key, b => b.Value);
Ответ 4
Я думаю, что ответ, который я выбрал изначально, по-прежнему является лучшим ответом для этого конкретного случая, я недавно оказался в другой подобной ситуации, когда у меня было 2 IEnumerable<>
объекта, которые я хотел преобразовать в словарь, и объединить их таким образом, я думал, что добавлю это решение здесь, чтобы помочь кому-то в будущем. Вместо того, чтобы конвертировать оба слова в словарь и использовать метод в выбранном ответе, я нашел новый подход.
Я опубликовал начальное решение на SE-CodeReview и на самом деле предложил уточнить его. Здесь последний код, который я использовал:
public Dictionary<String, Foo> Merge(XElement element1, XElement element2)
{
IEnumerable<Foo> firstFoos = GetXmlData(element1); // parse 1st set from XML
IEnumerable<Foo> secondFoos = GetXmlData(element2); // parse 2nd set from XML
var result = firstFoos.Union(secondFoos).ToDictionary(k=>k.Name, v=>v);
return result;
}
public class Foo
{
public String Name { get; }
// other Properties and Methods
// .
// .
// .
public override Boolean Equals(Object obj)
{
if (obj is Foo)
{
return this.Name == ((Foo)obj).Name;
}
return false;
}
}
Ключ к этому Foo
должен переопределить Equals()
, чтобы определить, какие объекты Foo
можно считать дублирующими, а члены, которые определяют, какие объекты дублируются, также должны быть ключом Dictionary<>
(в этом случае Name
)
Если вы не можете переопределить Equals()
в Foo
, то другой вариант должен использовать Concat()
и GroupBy()
вместо Union()
public Dictionary<String, Foo> Merge(XElement element1, XElement element2)
{
IEnumerable<Foo> firstFoos = GetXmlData(element1); // parse 1st set from XML
IEnumerable<Foo> secondFoos = GetXmlData(element2); // parse 2nd set from XML
var result = firstFoos.Concat(secondFoos)
.GroupBy(foo => foo.Name)
.Select(grp => grp.First())
.ToDictionary(k=>k.Name, v=>v);
return result;
}