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

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

19 голосов
/ 25 октября 2010

Я занимаюсь разработкой приложения на C # для .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? Я использую?

Ответы [ 4 ]

41 голосов
/ 25 октября 2010

Два очка:

  1. LINQ не подходит для выполнения побочных эффектов. В этом случае вы пытаетесь изменить существующую коллекцию, а не выполнить запрос, поэтому я бы уклонился от чистого решения LINQ.
  2. Установщик в индексаторе общего словаря уже имеет эффект добавления пары ключ-значение, если ключ не существует, или перезаписи значения, если оно существует.

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

Таким образом, ваш foreach цикл по сути эквивалентен:

foreach (var item in custom_settings)
{
   default_settings[item.Key] = item.Value;
}

Теперь это уже довольно кратко, поэтому я не думаю, что LINQ поможет вам во всем этом.

8 голосов
/ 21 февраля 2013

Вот хороший метод расширения, основанный на ответе Ани.

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;
        }
    }
}
2 голосов
/ 19 ноября 2010

Если вы собираетесь сделать это много, то я бы порекомендовал написать сравнитель равенства для ключей словаря:

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);
1 голос
/ 10 мая 2012

Я думаю, что ответ, который я выбрал изначально, все еще является лучшим ответом для этого конкретного случая, я недавно оказался в другой подобной ситуации, где у меня было 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;
}
...