Слияние словарей в C # - PullRequest
       75

Слияние словарей в C #

433 голосов
/ 16 ноября 2008

Как лучше всего объединить 2 или более словарей (Dictionary<T1,T2>) в C #? (3.0 функции, такие как LINQ, в порядке).

Я думаю о сигнатуре метода в соответствии с:

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(Dictionary<TKey,TValue>[] dictionaries);

или

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(IEnumerable<Dictionary<TKey,TValue>> dictionaries);

РЕДАКТИРОВАТЬ: Получил классное решение от JaredPar и Jon Skeet, но я думал о чем-то, что обрабатывает дубликаты ключей. В случае коллизии не имеет значения, какое значение сохранено в dict, если оно согласованно.

Ответы [ 23 ]

277 голосов
/ 16 ноября 2008

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

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

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

РЕДАКТИРОВАТЬ: Если вы используете ToLookup, то вы получите поиск, который может иметь несколько значений на ключ. Вы можете преобразовать это в словарь:

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

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

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

231 голосов
/ 14 июля 2011

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

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

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

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

93 голосов
/ 21 апреля 2010

Ну, я опаздываю на вечеринку, но вот что я использую. Он не взрывается, если есть несколько ключей («правильные» ключи заменяют «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<T,K,V>(this T me, params IDictionary<K,V>[] others)
        where T : IDictionary<K,V>, new()
    {
        T newMap = new T();
        foreach (IDictionary<K,V> src in
            (new List<IDictionary<K,V>> { me }).Concat(others)) {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K,V> p in src) {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

}
44 голосов
/ 16 ноября 2008

Тривиальное решение будет:

using System.Collections.Generic;
...
public static Dictionary<TKey, TValue>
    Merge<TKey,TValue>(IEnumerable<Dictionary<TKey, TValue>> dictionaries)
{
    var result = new Dictionary<TKey, TValue>();
    foreach (var dict in dictionaries)
        foreach (var x in dict)
            result[x.Key] = x.Value;
    return result;
}
20 голосов
/ 16 ноября 2008

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

static Dictionary<TKey, TValue>
    Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> enumerable)
{
    return enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value);
}
17 голосов
/ 06 апреля 2010
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);
14 голосов
/ 09 августа 2014

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

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(this IDictionary<TKey, TValue> dictA, IDictionary<TKey, TValue> dictB)
    where TValue : class
{
    return dictA.Keys.Union(dictB.Keys).ToDictionary(k => k, k => dictA.ContainsKey(k) ? dictA[k] : dictB[k]);
}
10 голосов
/ 08 июля 2014

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

foreach (KeyValuePair<string,int> item in D2)
            {
                 D1[item.Key] = item.Value;
            }

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

8 голосов
/ 06 августа 2009

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

using System.Collections.Generic;
namespace HelperMethods
{
    public static class MergeDictionaries
    {
        public static void Merge<TKey, TValue>(this IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second)
        {
            if (second == null || first == null) return;
            foreach (var item in second) 
                if (!first.ContainsKey(item.Key)) 
                    first.Add(item.Key, item.Value);
        }
    }
}
6 голосов
/ 16 ноября 2008

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

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

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(IEnumerable<IDictionary<TKey, TValue>> dictionaries)
{
    // ...
}

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(params IDictionary<TKey, TValue>[] dictionaries)
{
    return Merge((IEnumerable<TKey, TValue>) dictionaries);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...