C# Merging 2 dictionaries

家住魔仙堡 提交于 2019-11-30 08:09:23

Two points:

  1. LINQ isn't great for executing side effects. In this case, you're attempting to mutate an existing collection rather than execute a query, so I would shy away from a pure LINQ solution.
  2. The setter on the generic dictionary's indexer already has the effect of adding the key-value pair if the key doesn't exist, or overwriting the value if it does.

When you set the property value, if the key is in the Dictionary, the value associated with that key is replaced by the assigned value. If the key is not in the Dictionary, the key and value are added to the dictionary.

So your foreach loop is essentially equivalent to:

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

Now that's pretty terse already, so I don't think LINQ is going to help you all that much.

Here's a nice extension method based on Ani's answer.

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;
        }
    }
}

If your going to do this a lot, then I'd recommend writing an equality comparer for dictionary keys:

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();
    }
}

And then whenever you need to merge dictionaries, you can do the following

var comparer = new KeyEqualityComparer<string, MyElementSettings>();
dict1 = dict1.Union(dict2,comparer).ToDictionary(a => a.Key, b => b.Value);
psubsee2003

I think the answer I selected originally is still the best answer for this particular case, I found myself in another similar situation a little while ago where I had 2 IEnumerable<> objects that I wanted to convert to a dictionary and merge together in a similar fashion so I thought I would add that solution here to help someone in the future. Rather than converting both to a dictionary and using the method in the selected answer, I found a new approach.

I actually posted the initial solution on SE-CodeReview and actually had a suggestion to refine it further. Here's the final code I used:

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;
    }
}

The key to this is Foo must override Equals() to define what Foo objects can be considered duplicate, and the members that define which objects are duplicate should also be the Dictionary<> key (in this case Name)

If you can't override Equals() in Foo, then the other option is to use Concat() and GroupBy() instead of 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;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!