Объединение словарей в C #

Каков наилучший способ слияния 2 или более словарей ( Dictionary ) в C #? (3.0 функции, такие как LINQ, являются точными).

Я думаю о методе подписи:

 public static Dictionary Merge(Dictionary[] dictionaries); 

или

 public static Dictionary Merge(IEnumerable<Dictionary> dictionaries); 

EDIT: Получил крутое решение от JaredPar и Jon Skeet, но я думал о чем-то, что обрабатывает дубликаты ключей. В случае столкновения не имеет значения, какое значение сохраняется в dict, если оно согласовано.

Это отчасти зависит от того, что вы хотите, если вы столкнетесь с дубликатами. Например, вы можете:

 var result = dictionaries.SelectMany(dict => dict) .ToDictionary(pair => pair.Key, pair => pair.Value); 

Это взорвется, если вы получите дубликаты ключей.

EDIT: если вы используете ToLookup, вы получите поиск, который может иметь несколько значений для каждого ключа. Затем вы можете преобразовать это в словарь:

 var result = dictionaries.SelectMany(dict => dict) .ToLookup(pair => pair.Key, pair => pair.Value) .ToDictionary(group => group.Key, group => group.First()); 

Это немного уродливо – и неэффективно, но это самый быстрый способ сделать это с точки зрения кода. (Я не проверял это, правда).

Конечно, вы могли бы написать свой собственный метод расширения ToDictionary2 (с лучшим именем, но у меня нет времени подумать об этом сейчас) – это не очень сложно сделать, просто переписывая (или игнорируя) дубликаты ключей. Важный бит (на мой взгляд) использует SelectMany и понимает, что словарь поддерживает итерацию по своим парам ключ / значение.

Я бы сделал это так:

 dictionaryFrom.ToList().ForEach(x => dictionaryTo.Add(x.Key, x.Value)); 

Простой и легкий. Согласно этому сообщению в блоге, он даже быстрее, чем большинство циклов, поскольку его базовая реализация обращается к элементам по индексу, а не к перечислителю (см. Этот ответ) .

Разумеется, это будет исключение, если есть дубликаты, поэтому вам нужно будет проверить перед слиянием.

Ну, я опаздываю на вечеринку, но вот что я использую. Он не взрывается, если есть несколько ключей («более быстрые» клавиши заменяют «lefter»), могут объединять несколько словарей (при желании) и сохранять тип (с ограничением, для которого требуется полноценный стандартный конструктор по умолчанию):

 public static class DictionaryExtensions { // Works in C#3/VS2008: // Returns a new dictionary of this ... others merged leftward. // Keeps the type of 'this', which must be default-instantiable. // Example: // result = map.MergeLeft(other1, other2, ...) public static T MergeLeft(this T me, params IDictionary[] others) where T : IDictionary, new() { T newMap = new T(); foreach (IDictionary src in (new List> { me }).Concat(others)) { // ^-- echk. Not quite there type-system. foreach (KeyValuePair p in src) { newMap[p.Key] = p.Value; } } return newMap; } } 

Тривиальное решение было бы:

 using System.Collections.Generic; ... public static Dictionary Merge(IEnumerable> dictionaries) { var result = new Dictionary(); foreach (var dict in dictionaries) foreach (var x in dict) result[x.Key] = x.Value; return result; } 
 Dictionary allTables = new Dictionary(); allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value); 

Попробуйте следующее

 static Dictionary Merge(this IEnumerable> enumerable) { return enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value); } 

Для меня работает следующее. Если есть дубликаты, он будет использовать значение dictA.

 public static IDictionary Merge(this IDictionary dictA, IDictionary dictB) where TValue : class { return dictA.Keys.Union(dictB.Keys).ToDictionary(k => k, k => dictA.ContainsKey(k) ? dictA[k] : dictB[k]); } 

Вот вспомогательная функция, которую я использую:

 using System.Collections.Generic; namespace HelperMethods { public static class MergeDictionaries { public static void Merge(this IDictionary first, IDictionary second) { if (second == null || first == null) return; foreach (var item in second) if (!first.ContainsKey(item.Key)) first.Add(item.Key, item.Value); } } } 

Как насчет добавления перегрузки params ?

Кроме того, вы должны ввести их как IDictionary для максимальной гибкости.

 public static IDictionary Merge(IEnumerable> dictionaries) { // ... } public static IDictionary Merge(params IDictionary[] dictionaries) { return Merge((IEnumerable) dictionaries); } 

Я очень опаздываю на вечеринку и, возможно, что-то пропущу, но если у вас нет дубликатов ключей или, как говорит OP, «В случае столкновения не имеет значения, какое значение сохраняется в dict, если оно «что не так с этим (слияние D2 в D1)?

 foreach (KeyValuePair item in D2) { D1[item.Key] = item.Value; } 

Это кажется достаточно простым, может быть, слишком простым, мне интересно, не хватает ли я чего-то. Это то, что я использую в некотором коде, где я знаю, что нет дубликатов ключей. Тем не менее, я все еще в тестировании, поэтому мне бы хотелось узнать сейчас, если я что-то пропустил, вместо того, чтобы узнать позже.

Учитывая эффективность поиска и удаления словарных ключей, поскольку они являются хеш-операциями, и учитывая, что формулировка вопроса является наилучшим способом, я считаю, что ниже – это совершенно правильный подход, а другие немного сложнее, ИМХО.

  public static void MergeOverwrite(this IDictionary dictionary, IDictionary newElements) { if (newElements == null) return; foreach (var e in newElements) { dictionary.Remove(e.Key); //or if you don't want to overwrite do (if !.Contains() dictionary.Add(e); } } 

ИЛИ, если вы работаете в многопоточном приложении, и ваш словарь должен быть streamобезопасным, вы должны это сделать:

  public static void MergeOverwrite(this ConcurrentDictionary dictionary, IDictionary newElements) { if (newElements == null || newElements.Count == 0) return; foreach (var ne in newElements) { dictionary.AddOrUpdate(ne.Key, ne.Value, (key, value) => value); } } 

Затем вы можете обернуть это, чтобы обработать список словарей. Независимо от того, что вы смотрите примерно на ~ O (3n) (все условия являются совершенными), поскольку .Add() сделает дополнительный, ненужный, но практически свободный, Contains() за кулисами. Я не думаю, что это становится намного лучше.

Если вы хотите ограничить дополнительные операции над большими коллекциями, вы должны подытожить количество каждого словаря, который вы собираетесь объединить, и установить емкость целевого словаря для этого, что позволит избежать более поздней стоимости изменения размера. Итак, конечный продукт – это что-то вроде этого …

  public static IDictionary MergeAllOverwrite(IList> allDictionaries) { var initSize = allDictionaries.Sum(d => d.Count); var resultDictionary = new Dictionary(initSize); allDictionaries.ForEach(resultDictionary.MergeOverwrite); return resultDictionary; } 

Обратите внимание, что я использовал IList для этого метода … главным образом потому, что если вы возьмете IEnumerable , вы открыли себя до нескольких перечислений одного и того же набора, что может быть очень дорогостоящим, если вы получили ваша коллекция словарей из отложенного оператора LINQ.

Основываясь на ответах выше, но добавляя параметр Func, чтобы вызывающий вызывал дубликаты:

 public static Dictionary Merge(this IEnumerable> dicts, Func, TValue> resolveDuplicates) { if (resolveDuplicates == null) resolveDuplicates = new Func, TValue>(group => group.First()); return dicts.SelectMany, KeyValuePair>(dict => dict) .ToLookup(pair => pair.Key, pair => pair.Value) .ToDictionary(group => group.Key, group => resolveDuplicates(group)); } 

В настоящее время партия почти мертва, но вот «улучшенная» версия user166390, которая пробилась в мою библиотеку расширений. Помимо некоторых деталей, я добавил делегата для вычисления объединенного значения.

 ///  /// Merges a dictionary against an array of other dictionaries. ///  /// The type of the resulting dictionary. /// The type of the key in the resulting dictionary. /// The type of the value in the resulting dictionary. /// The source dictionary. /// A delegate returning the merged value. (Parameters in order: The current key, The current value, The previous value) /// Dictionaries to merge against. /// The merged dictionary. public static TResult MergeLeft( this TResult source, Func mergeBehavior, params IDictionary[] mergers) where TResult : IDictionary, new() { var result = new TResult(); var sources = new List> { source } .Concat(mergers); foreach (var kv in sources.SelectMany(src => src)) { TValue previousValue; result.TryGetValue(kv.Key, out previousValue); result[kv.Key] = mergeBehavior(kv.Key, kv.Value, previousValue); } return result; } 

@Tim: Должен быть комментарий, но комментарии не позволяют редактировать код.

 Dictionary t1 = new Dictionary(); t1.Add("a", "aaa"); Dictionary t2 = new Dictionary(); t2.Add("b", "bee"); Dictionary t3 = new Dictionary(); t3.Add("c", "cee"); t3.Add("d", "dee"); t3.Add("b", "bee"); Dictionary merged = t1.MergeLeft(t2, t2, t3); 

Примечание. Я применил модификацию @ANeves к решению @Andrew Orsich, поэтому теперь MergeLeft выглядит следующим образом:

 public static Dictionary MergeLeft(this Dictionary me, params IDictionary[] others) { var newMap = new Dictionary(me, me.Comparer); foreach (IDictionary src in (new List> { me }).Concat(others)) { // ^-- echk. Not quite there type-system. foreach (KeyValuePair p in src) { newMap[p.Key] = p.Value; } } return newMap; } 

Я знаю, что это старый вопрос, но поскольку теперь у нас есть LINQ, вы можете сделать это в одной строке, подобной этой

 Dictionary merged; Dictionary mergee; mergee.ToList().ForEach(kvp => merged.Add(kvp.Key, kvp.Value)); 

или

 mergee.ToList().ForEach(kvp => merged.Append(kvp)); 

Слияние с использованием метода расширения. Он не генерирует исключение, если есть дубликаты ключей, но заменяет эти ключи клавишами из второго словаря.

 internal static class DictionaryExtensions { public static Dictionary Merge(this Dictionary first, Dictionary second) { if (first == null) throw new ArgumentNullException("first"); if (second == null) throw new ArgumentNullException("second"); var merged = new Dictionary(); first.ToList().ForEach(kv => merged[kv.Key] = kv.Value); second.ToList().ForEach(kv => merged[kv.Key] = kv.Value); return merged; } } 

Применение:

 Dictionary merged = first.Merge(second); 

Объединение с помощью EqualityComparer которое сопоставляет элементы для сравнения с другим значением / типом. Здесь мы перейдем к KeyValuePair (тип элемента при перечислении словаря) в Key .

 public class MappedEqualityComparer : EqualityComparer { Func _map; public MappedEqualityComparer(Func map) { _map = map; } public override bool Equals(T x, T y) { return EqualityComparer.Default.Equals(_map(x), _map(y)); } public override int GetHashCode(T obj) { return _map(obj).GetHashCode(); } } 

Применение:

 // if dictA and dictB are of type Dictionary var dict = dictA.Concat(dictB) .Distinct(new MappedEqualityComparer,int>(item => item.Key)) .ToDictionary(item => item.Key, item=> item.Value); 

или :

 public static IDictionary Merge( IDictionary x, IDictionary y) { return x .Except(x.Join(y, z => z.Key, z => z.Key, (a, b) => a)) .Concat(y) .ToDictionary(z => z.Key, z => z.Value); } 

результатом является объединение, в котором выигрывает двойная запись «y».

  • Итерация через словарь в Swift
  • C # JSON Сериализация словаря в {key: value, ...} вместо {key: key, value: value, ...}
  • Случайная запись из словаря
  • В чем разница между объектами HashMap и Map в Java?
  • Являются ли словари упорядоченными в Python 3.6+?
  • Как я могу инициализировать статическую карту?
  • Словарь VBA (Excel) на Mac?
  • Пересечение java.util.Map
  • Кортежи (или массивы) в качестве словарных ключей в C #
  • Преобразование списка кортежей в словарь
  • Почему не удается выполнить итерацию карт в порядке ввода?
  • Interesting Posts

    Как я могу исправить компьютер, зараженный вредоносным ПО, и крайне не отвечает?

    Как включить / отключить контроллер ethernet из командной строки

    Как отобразить индикатор активности с текстом на iOS 8 с помощью Swift?

    В чем разница между быстрым и полным форматом?

    Для чего нужны пространства имен XML?

    Можно ли заставить SSH передавать пустые аргументы?

    java.lang.OutOfMemoryError: пространство кучи Java в Maven

    swift Сделайте снимок и сохраните его в библиотеке фотографий

    Можно ли использовать сетевой разделитель для подключения маршрутизатора к двум устройствам?

    Объединение двух изображений

    Как подтвердить, что почта была доставлена ​​или нет?

    Могу ли я получить доступ к архивам Microsoft Outlook PST с помощью любых других инструментов?

    Понимание защищенного Java-модификатора

    Перетаскивание элементов в подпапки с рабочего стола без открытия окна проводника?

    Является ли Windows кэшированием URL-адресов файлов ярлыков Интернета?

    Давайте будем гением компьютера.