Словарь слияния, содержащий список в C # - PullRequest
3 голосов
/ 25 января 2010

Это как-то связано с этим вопросом о том, как объединить два словаря в C #. Представлено элегантное решение Linq, которое классно.

Однако этот вопрос относится к Dictionary<Object1, Object2>,, тогда как у меня есть словарь, в котором значение равно List<Object2>.

Я ищу решение для объединения Dictionary<Object1, List<Object2>>, со следующими требованиями:

  • Если Dictionary1 содержит тот же ключ, что и Dictionary2, их списки List<Object2> должны быть объединены. В итоге вы получите новую пару ключ-значение с общим ключом и объединенные списки из двух словарей.
  • Если Dictionary1 содержит ключ, которого Dictionary2 не имеет, тогда значение List<Object2> из Dictionary1 должно стать значением, и наоборот.

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

Ответы [ 4 ]

3 голосов
/ 25 января 2010

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

public static void MergeDictionaries<OBJ1, OBJ2>(this IDictionary<OBJ1, List<OBJ2>> dict1, IDictionary<OBJ1, List<OBJ2>> dict2)
    {
        foreach (var kvp2 in dict2)
        {
            // If the dictionary already contains the key then merge them
            if (dict1.ContainsKey(kvp2.Key))
            {
                dict1[kvp2.Key].AddRange(kvp2.Value);
                continue;
            }
            dict1.Add(kvp2);
        }
    }
2 голосов
/ 25 января 2010

Вам просто нужно изменить деталь слияния в решении предыдущей проблемы. Для объекта у нас есть это:

.ToDictionary(group => group.Key, group => group.First())

т.е. для дублированных предметов просто возьмите первый.

Но мы могли бы использовать это:

.ToDictionary(group => group.Key, group => group.SelectMany(list => list).ToList());

для объединения списков.

Итак, окончательное выражение будет

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

Вы можете попробовать другое выражение слияния, если вам нужна дополнительная логика слияния списков (например, объединять только отдельные элементы)

2 голосов
/ 25 января 2010

Трудность связана с объединением ключевых конфликтов.

Если мы начнем с выравнивания всех входных словарей, используя SelectMany, мы можем сгруппировать элементы по их ключу.

var result = dictionaries
    .SelectMany(dict => dict)
    .GroupBy(kvp => kvp.Key)

Набор результатов содержит группы, в которых ключ каждой группы является ключом из исходных словарей, а содержимое группы представляет собой IEnumerable<List<T>> списков с одинаковым ключом. Из этих групп мы можем объединить все List<T> в один IEnumerable<T>, используя преобразование Select с SelectMany.

var result = dictionaries
    .SelectMany(dict => dict)
    .GroupBy(kvp => kvp.Key)
    .Select(grp => new { Key = grp.Key, Items = grp.SelectMany(list => list)})

Затем мы можем получить из этого словарь, используя преобразование ToDictionary, преобразовав IEnumerable<T> обратно в List<T>.

var result = dictionaries
    .SelectMany(dict => dict)
    .GroupBy(kvp => kvp.Key)
    .Select(grp => new { Key = grp.Key, Items = grp.SelectMany(list => list)})
    .ToDictionary(kip => kip.Key, kip => new List<T>(kip.Items));

Обновлено в ответ на комментарий

Вы можете заполнить dictionaries так, как вам нравится. Я предположил, что это тип, который реализует IEnumerable<IDictionary<TKey, List<T>>> для TKey и T по вашему выбору.

Самый простой способ - использовать List<T> следующим образом:

List<IDictionary<TKey, List<T>>> dictionaries 
    = new List<IDictionary<TKey, List<T>>>();

dictionaries.Add(dictionary1); // Your variable
dictionaries.Add(dictionary2); // Your variable

// Add any other dictionaries here.

// Code as above!
1 голос
/ 25 января 2010

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

var d1 = new Dictionary<string, List<string>>();
var d2 = new Dictionary<string, List<string>>();

d1["test"] = new List<string>() { "Stockholm", "Motala" };
d1["more"] = new List<string>() { "numerous", "populous", "bigger", "plentiful" };
d2["test"] = new List<string>() { "Washington", "Charlottesville" };
d2["less"] = new List<string>() { "insufficient", "small", "imperceptible" };

var intersect = (from key in d1.Keys.Intersect(d2.Keys) select new { Key = key, Value = new List<string>(d1[key].Concat(d2[key])) }).ToDictionary(d => d.Key, d => d.Value);
var merged = d1.Concat(d2).Where(d => !intersect.Keys.Contains(d.Key)).Concat(intersect).ToDictionary(d => d.Key, d => d.Value);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...