Merging dictionaries in C#

前端 未结 26 1036
温柔的废话
温柔的废话 2020-11-22 08:25

What\'s the best way to merge 2 or more dictionaries (Dictionary) in C#? (3.0 features like LINQ are fine).

I\'m thinking of a method signa

相关标签:
26条回答
  • 2020-11-22 08:50

    Considering the performance of dictionary key lookups and deletes since they are hash operations, and considering the wording of the question was best way, I think that below is a perfectly valid approach, and the others are a bit over-complicated, IMHO.

        public static void MergeOverwrite<T1, T2>(this IDictionary<T1, T2> dictionary, IDictionary<T1, T2> 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);
            }
        }
    

    OR if you're working in a multithreaded application and your dictionary needs to be thread safe anyway, you should be doing this:

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

    You could then wrap this to make it handle an enumeration of dictionaries. Regardless, you're looking at about ~O(3n) (all conditions being perfect), since the .Add() will do an additional, unnecessary but practically free, Contains() behind the scenes. I don't think it gets much better.

    If you wanted to limit extra operations on large collections, you should sum up the Count of each dictionary you're about to merge and set the capacity of the the target dictionary to that, which avoids the later cost of resizing. So, end product is something like this...

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

    Note that I took in an IList<T> to this method... mostly because if you take in an IEnumerable<T>, you've opened yourself up to multiple enumerations of the same set, which can be very costly if you got your collection of dictionaries from a deferred LINQ statement.

    0 讨论(0)
  • 2020-11-22 08:51

    The trivial solution would be:

    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;
    }
    
    0 讨论(0)
  • 2020-11-22 08:51
    fromDic.ToList().ForEach(x =>
            {
                if (toDic.ContainsKey(x.Key))
                    toDic.Remove(x.Key);
                toDic.Add(x);
            });
    
    0 讨论(0)
  • 2020-11-22 08:54

    The party's pretty much dead now, but here's an "improved" version of user166390 that made its way into my extension library. Apart from some details, I added a delegate to calculate the merged value.

    /// <summary>
    /// Merges a dictionary against an array of other dictionaries.
    /// </summary>
    /// <typeparam name="TResult">The type of the resulting dictionary.</typeparam>
    /// <typeparam name="TKey">The type of the key in the resulting dictionary.</typeparam>
    /// <typeparam name="TValue">The type of the value in the resulting dictionary.</typeparam>
    /// <param name="source">The source dictionary.</param>
    /// <param name="mergeBehavior">A delegate returning the merged value. (Parameters in order: The current key, The current value, The previous value)</param>
    /// <param name="mergers">Dictionaries to merge against.</param>
    /// <returns>The merged dictionary.</returns>
    public static TResult MergeLeft<TResult, TKey, TValue>(
        this TResult source,
        Func<TKey, TValue, TValue, TValue> mergeBehavior,
        params IDictionary<TKey, TValue>[] mergers)
        where TResult : IDictionary<TKey, TValue>, new()
    {
        var result = new TResult();
        var sources = new List<IDictionary<TKey, TValue>> { 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;
    }
    
    0 讨论(0)
  • Try the following

    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);
    }
    
    0 讨论(0)
  • 2020-11-22 08:56
    Dictionary<String, String> allTables = new Dictionary<String, String>();
    allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);
    
    0 讨论(0)
提交回复
热议问题