Hợp nhất từ ​​điển trong C #


493

Cách tốt nhất để hợp nhất 2 hoặc nhiều từ điển ( Dictionary<T1,T2>) trong C # là gì? (3.0 tính năng như LINQ đều ổn).

Tôi đang nghĩ về một chữ ký phương thức dọc theo dòng:

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

hoặc là

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

EDIT: Có một giải pháp tuyệt vời từ JaredPar và Jon Skeet, nhưng tôi đã nghĩ đến một cái gì đó xử lý các khóa trùng lặp. Trong trường hợp va chạm, không quan trọng giá trị nào được lưu vào dict miễn là nó phù hợp.


174
Không liên quan, nhưng đối với bất kỳ ai đang tìm cách hợp nhất chỉ hai từ điển mà không cần kiểm tra khóa trùng lặp, điều này hoạt động độc đáo:dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
Stewol

6
@Benjol bạn có thể đã thêm phần này vào phần trả lời
M.Kumaran

4
Hợp nhất của Clojure trong C #:dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
Bruce

@Benjol: loại bỏ trùng lặp bằng cách ưu tiên các mục từ từ điển đầu tiên với dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value).
Suncat2000

Câu trả lời:


321

Điều này một phần phụ thuộc vào những gì bạn muốn xảy ra nếu bạn gặp phải các bản sao. Chẳng hạn, bạn có thể làm:

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

Điều đó sẽ ném một ngoại lệ nếu bạn nhận được bất kỳ khóa trùng lặp.

EDIT: Nếu bạn sử dụng ToLookup thì bạn sẽ có một tra cứu có thể có nhiều giá trị cho mỗi khóa. Sau đó, bạn có thể chuyển đổi nó thành một từ điển:

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

Nó hơi xấu - và không hiệu quả - nhưng đó là cách nhanh nhất để làm điều đó về mặt mã. (Tôi đã không kiểm tra nó, thừa nhận.)

Tất nhiên, bạn có thể viết phương thức mở rộng ToDipedia2 của riêng mình (với tên hay hơn, nhưng tôi không có thời gian để nghĩ về nó bây giờ) - nó không quá khó để thực hiện, chỉ cần ghi đè (hoặc bỏ qua) các khóa trùng lặp. Điều quan trọng (theo suy nghĩ của tôi) là sử dụng SelectMany và nhận ra rằng một từ điển hỗ trợ việc lặp qua các cặp khóa / giá trị của nó.


3
Để thực sự hợp nhất các giá trị thay vì chỉ lấy chúng từ từ điển đầu tiên, bạn có thể thay thế nhóm => group.First () bằng nhóm => group.SelectMany (value => value) trong câu trả lời được chỉnh sửa của Jon Skeet.
andreialecu

3
Bây giờ tôi đang tự hỏi rằng GroupBy sẽ phù hợp hơn ToLookup? Tôi nghĩ ILookup chỉ cần thêm một bộ chỉ mục, thuộc tính kích thước và chứa phương thức trên IGrouping - vì vậy nó phải nhanh hơn một chút?
toong

2
@toong: Hoặc là ổn, phải trung thực. Sử dụng GroupBythực sự có thể hiệu quả hơn một chút - nhưng tôi nghi ngờ rằng nó cũng có ý nghĩa.
Jon Skeet

1
@Joe: Điều đó đủ đơn giản bằng cách cung cấp trình so sánh với ToDictionarylệnh gọi phương thức. Tuy nhiên, tôi muốn giữ câu trả lời đơn giản hơn cho trường hợp phổ biến hơn và tôi hy vọng rằng bất kỳ ai cần một bộ so sánh tùy chỉnh đều có khả năng tìm thấy sự quá tải có liên quan.
Jon Skeet

1
@Serge: Tôi đề nghị bạn hỏi một câu hỏi mới với các yêu cầu chính xác.
Jon Skeet

265

Tôi sẽ làm như thế này:

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

Đơn giản và dễ dàng. Theo bài đăng trên blog này, nó thậm chí còn nhanh hơn hầu hết các vòng lặp vì việc triển khai cơ bản của nó truy cập các phần tử theo chỉ mục thay vì liệt kê (xem câu trả lời này) .

Tất nhiên nó sẽ ném một ngoại lệ nếu có trùng lặp, vì vậy bạn sẽ phải kiểm tra trước khi hợp nhất.


169
Nếu trùng lặp vấn đề, sử dụng dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value). Bằng cách này, các giá trị từ dictionaryFromghi đè giá trị cho khóa có thể có.
okrumnow

7
Điều này dường như không nhanh hơn một vòng lặp - bạn quên rằng ToList () thực sự kết thúc việc sao chép từ điển. Nhanh hơn để mã mặc dù! ;-)
Søren Boisen

6
Việc này nhanh hơn .. Vì ToList () thực sự không sao chép từ điển, nên nó chỉ sao chép các tham chiếu của các phần tử bên trong chúng. Về cơ bản, chính đối tượng danh sách sẽ có một địa chỉ mới trong bộ nhớ, các đối tượng đều giống nhau !!
GuruC

4
Blog bài biến mất nhưng Yay máy Wayback: web.archive.org/web/20150311191313/http://diditwith.net/2006/10/...
CAD bloke

1
Hmm, tốt hơn so với các câu trả lời của Jon Skeet, tôi đoán.
Sнаđошƒаӽ

102

Điều này không phát nổ nếu có nhiều khóa (khóa "sáng hơn" thay thế các phím "lefter"), có thể hợp nhất một số từ điển (nếu muốn) và giữ nguyên loại (với hạn chế là nó yêu cầu một hàm tạo công khai mặc định có ý nghĩa):

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

}

Làm tốt. Đây chính xác là những gì tôi cần. Sử dụng tốt đẹp của thuốc generic. Đồng ý rằng cú pháp hơi khó xử, nhưng bạn không thể làm gì về nó.
Peter M

2
Tôi thích giải pháp này, nhưng có một cảnh báo: nếu this T metừ điển được khai báo sử dụng new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)hoặc một cái gì đó tương tự, từ điển kết quả sẽ không giữ lại hành vi tương tự.
João Portela

3
Nếu bạn thực hiện memột Dictionary<K,V>, sau đó bạn có thể làm var newMap = new Dictionary<TKey, TValue>(me, me.Comparer);, và bạn cũng tránh Ttham số loại thêm .
ANeves

1
Có ai có một ví dụ về cách sử dụng con thú này?
Tim

1
@Tim: Tôi đã thêm một ví dụ cho con thú bên dưới, tôi đã thêm giải pháp của ANeves và kết quả là một con thú được thuần hóa nhiều hơn :)
keni

50

Giải pháp tầm thường sẽ là:

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

22

Hãy thử như sau

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

19
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);

1
Chỉ cần tự hỏi - không chỉ Liên minh sẽ làm việc? foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))Sử dụng nó trong mã sản xuất, nếu có bất kỳ nhược điểm nào xin vui lòng cho tôi biết! :)
Mars Robertson

1
@Michal: Union sẽ trả về một IEnumerable<KeyValuePair<TKey, TValue>>nơi mà sự bình đẳng được xác định bởi sự bằng nhau của khóa và giá trị (có sự quá tải để cho phép thay thế). Điều này tốt cho một vòng lặp foreach, trong đó đây là tất cả những gì cần thiết, nhưng có những trường hợp bạn thực sự muốn từ điển.
Ti-mô-thê

15

Tôi đến bữa tiệc rất muộn và có lẽ thiếu một cái gì đó, nhưng nếu không có khóa trùng lặp hoặc, như OP nói, "Trong trường hợp va chạm, không có vấn đề gì được lưu vào giá trị cho đến khi nào nhất quán, "có gì sai với cái này (sáp nhập D2 vào D1)?

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

Có vẻ như đủ đơn giản, có thể quá đơn giản, tôi tự hỏi nếu tôi thiếu một cái gì đó. Đây là những gì tôi đang sử dụng trong một số mã mà tôi biết không có khóa trùng lặp. Mặc dù vậy, tôi vẫn đang trong quá trình thử nghiệm, vì vậy tôi rất muốn biết bây giờ nếu tôi đang xem xét một cái gì đó, thay vì tìm hiểu sau này.


4
Một trong những giải pháp sạch nhất và dễ đọc nhất cũng như tránh ToList () mà nhiều người khác sử dụng.
404

15

Các công việc sau đây cho tôi. Nếu có trùng lặp, nó sẽ sử dụng giá trị của 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]);
}

@EsbenSkovPedersen Tôi không nghĩ rằng tôi đã gặp phải điều đó khi tôi trả lời điều này (C # dev mới)
Ethan Reesor

Tôi đã phải xóa 'where TValue: class' để sử dụng Guid làm giá trị
om471987

@ om471987 Vâng, Guid là một cấu trúc không phải là một lớp, vì vậy điều đó có ý nghĩa. Tôi nghĩ ban đầu tôi có một số vấn đề khi tôi không thêm điều khoản đó. Đừng nhớ tại sao nữa.
Ethan Reesor

10

Đây là một chức năng trợ giúp tôi sử dụng:

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

Có rất nhiều giải pháp tuyệt vời được liệt kê ở đây, nhưng tôi thích sự đơn giản nhất của giải pháp này. Chỉ là sở thích cá nhân của tôi.
jason

3
if(first == null)thì logic này là vô dụng vì firstkhông refvà không được trả lại. Và không thể refvì nó được tuyên bố là một thể hiện giao diện; bạn cần loại bỏ việc kiểm tra lỗi hoặc khai báo nó như một thể hiện của lớp hoặc chỉ trả về một từ điển mới (không có phương thức mở rộng).
Grault

@Jesdiscipl - đã xóa vấn đề theo đề nghị của bạn
Andrew Harry

8

Tùy chọn 1: Điều này phụ thuộc vào những gì bạn muốn xảy ra nếu bạn chắc chắn rằng bạn không có khóa trùng lặp trong cả hai từ điển. hơn bạn có thể làm:

var result = dictionary1.Union(dictionary2).ToDictionary(k => k.Key, v => v.Value)

Lưu ý: Điều này sẽ gây ra lỗi nếu bạn nhận được bất kỳ khóa trùng lặp nào trong từ điển.

Tùy chọn 2: Nếu bạn có thể có khóa trùng lặp thì bạn sẽ phải xử lý khóa trùng lặp với việc sử dụng mệnh đề where.

var result = dictionary1.Union(dictionary2.Where(k => !dictionary1.ContainsKey(k.Key))).ToDictionary(k => k.Key, v => v.Value)

Lưu ý: Nó sẽ không nhận được khóa trùng lặp. nếu có bất kỳ khóa trùng lặp nào thì nó sẽ lấy khóa dictionary1.

Tùy chọn 3: Nếu bạn muốn sử dụng ToLookup. sau đó bạn sẽ nhận được một tra cứu có thể có nhiều giá trị cho mỗi khóa. Bạn có thể chuyển đổi tra cứu đó thành một từ điển:

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

6

Làm thế nào về việc thêm một paramsquá tải?

Ngoài ra, bạn nên gõ chúng như IDictionaryđể linh hoạt tối đa.

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

4
Anh ấy thêm vào các câu trả lời khác ở đây bằng cách lưu ý rằng bạn có thể làm cho lớp DictionaryExtensions thậm chí còn đẹp hơn. Có lẽ câu hỏi này sẽ trở thành một wiki hoặc một lớp được đăng ký để git ....
Chris Moschini

5

Xem xét hiệu suất của việc tra cứu và xóa khóa từ điển vì chúng là các thao tác băm và xem xét từ ngữ của câu hỏi là cách tốt nhất , tôi nghĩ rằng dưới đây là một cách tiếp cận hoàn toàn hợp lệ và các phương pháp khác hơi phức tạp, 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);
        }
    }

HOẶC nếu bạn đang làm việc trong một ứng dụng đa luồng và từ điển của bạn cần phải an toàn cho chủ đề, bạn nên làm điều này:

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

Sau đó, bạn có thể bọc cái này để làm cho nó xử lý một bảng liệt kê từ điển. Bất kể, bạn đang nhìn vào khoảng ~ O (3n) (tất cả các điều kiện là hoàn hảo), vì .Add()sẽ làm thêm, không cần thiết nhưng thực tế miễn phí, Contains()đằng sau hậu trường. Tôi không nghĩ rằng nó sẽ tốt hơn nhiều.

Nếu bạn muốn giới hạn các thao tác bổ sung trên các bộ sưu tập lớn, bạn nên tổng hợp Counttừng từ điển mà bạn sắp hợp nhất và đặt dung lượng của từ điển đích vào đó, để tránh chi phí thay đổi kích thước sau này. Vì vậy, sản phẩm cuối cùng là một cái gì đó như thế này ...

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

Lưu ý rằng tôi đã áp dụng IList<T>phương pháp này ... chủ yếu là vì nếu bạn tham gia IEnumerable<T>, bạn đã tự mở ra nhiều bảng liệt kê của cùng một bộ, điều này có thể rất tốn kém nếu bạn có bộ sưu tập từ điển của mình từ một LINQ bị hoãn tuyên bố.


4

Dựa trên các câu trả lời ở trên, nhưng thêm tham số Func để cho phép người gọi xử lý các bản sao:

public static Dictionary<TKey, TValue> Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> dicts, 
                                                           Func<IGrouping<TKey, TValue>, TValue> resolveDuplicates)
{
    if (resolveDuplicates == null)
        resolveDuplicates = new Func<IGrouping<TKey, TValue>, TValue>(group => group.First());

    return dicts.SelectMany<Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue>>(dict => dict)
                .ToLookup(pair => pair.Key, pair => pair.Value)
                .ToDictionary(group => group.Key, group => resolveDuplicates(group));
}

4

Hiện tại nhóm đã chết khá nhiều, nhưng đây là phiên bản "cải tiến" của user166390 được đưa vào thư viện tiện ích mở rộng của tôi. Ngoài một số chi tiết, tôi đã thêm một đại biểu để tính giá trị hợp nhất.

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

2

@Tim: Nên là một bình luận, nhưng các bình luận không cho phép chỉnh sửa mã.

Dictionary<string, string> t1 = new Dictionary<string, string>();
t1.Add("a", "aaa");
Dictionary<string, string> t2 = new Dictionary<string, string>();
t2.Add("b", "bee");
Dictionary<string, string> t3 = new Dictionary<string, string>();
t3.Add("c", "cee");
t3.Add("d", "dee");
t3.Add("b", "bee");
Dictionary<string, string> merged = t1.MergeLeft(t2, t2, t3);

Lưu ý: Tôi đã áp dụng sửa đổi bởi @ANeves cho giải pháp của @Andrew Orsich, vì vậy MergeLeft trông giống như bây giờ:

public static Dictionary<K, V> MergeLeft<K, V>(this Dictionary<K, V> me, params IDictionary<K, V>[] others)
    {
        var newMap = new Dictionary<K, V>(me, me.Comparer);
        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;
    }

Gần với những gì tôi đã viết cho mình. Điều này, ofc, sẽ chỉ làm việc cho Dictionarycác loại. Quá tải có thể được thêm vào cho các loại Từ điển khác ( ConcurrentDictionary, ReadOnlyDictionaryv.v.) Tôi không nghĩ việc tạo một danh sách mới và cá nhân là cần thiết. Tôi chỉ lặp lại mảng params trước, sau đó lặp lại từng KVP của mỗi từ điển. Tôi không thấy một sự rút lại.
nghiền nát

Bạn có thể sử dụng IDixi thay vì Dictionary
Mariusz Jamro

Bạn sẽ luôn nhận được một Dictionaryđối tượng ngay cả khi bạn đã sử dụng phương thức mở rộng trên a ConcurrentDictionary. Điều đó có thể dẫn đến khó theo dõi lỗi.
Marcel

2

Tôi biết đây là một câu hỏi cũ, nhưng vì bây giờ chúng tôi có LINQ, bạn có thể thực hiện nó trong một dòng như thế này

Dictionary<T1,T2> merged;
Dictionary<T1,T2> mergee;
mergee.ToList().ForEach(kvp => merged.Add(kvp.Key, kvp.Value));

hoặc là

mergee.ToList().ForEach(kvp => merged.Append(kvp));

Xin lỗi vì downvote @Cruces, nhưng ví dụ đầu tiên của bạn trùng lặp câu trả lời stackoverflow.com/a/6695211/704808 và ví dụ thứ hai của bạn chỉ tiếp tục loại bỏ IEnumerable <KeyValuePair <chuỗi, chuỗi >> sau khi nối từng mục từ mergee.
đập vào

2

Đã sợ khi thấy câu trả lời phức tạp, là người mới đối với C #.

Dưới đây là một số câu trả lời đơn giản.
Hợp nhất d1, d2, v.v. từ điển và xử lý bất kỳ khóa chồng chéo nào ("b" trong các ví dụ dưới đây):

ví dụ 1

{
    // 2 dictionaries,  "b" key is common with different values

    var d1 = new Dictionary<string, int>() { { "a", 10 }, { "b", 21 } };
    var d2 = new Dictionary<string, int>() { { "c", 30 }, { "b", 22 } };

    var result1 = d1.Concat(d2).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.First().Value);
    // result1 is  a=10, b=21, c=30    That is, took the "b" value of the first dictionary

    var result2 = d1.Concat(d2).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.Last().Value);
    // result2 is  a=10, b=22, c=30    That is, took the "b" value of the last dictionary
}

Ví dụ 2

{
    // 3 dictionaries,  "b" key is common with different values

    var d1 = new Dictionary<string, int>() { { "a", 10 }, { "b", 21 } };
    var d2 = new Dictionary<string, int>() { { "c", 30 }, { "b", 22 } };
    var d3 = new Dictionary<string, int>() { { "d", 40 }, { "b", 23 } };

    var result1 = d1.Concat(d2).Concat(d3).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.First().Value);
    // result1 is  a=10, b=21, c=30, d=40    That is, took the "b" value of the first dictionary

    var result2 = d1.Concat(d2).Concat(d3).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.Last().Value);
    // result2 is  a=10, b=23, c=30, d=40    That is, took the "b" value of the last dictionary
}

Đối với các kịch bản phức tạp hơn, xem câu trả lời khác.
Hy vọng rằng đã giúp.


2
using System.Collections.Generic;
using System.Linq;

public static class DictionaryExtensions
{
    public enum MergeKind { SkipDuplicates, OverwriteDuplicates }
    public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, MergeKind kind = MergeKind.SkipDuplicates) =>
        source.ToList().ForEach(_ => { if (kind == MergeKind.OverwriteDuplicates || !target.ContainsKey(_.Key)) target[_.Key] = _.Value; });
}

Bạn có thể bỏ qua / bỏ qua (mặc định) hoặc ghi đè lên các bản sao: Và chú của Bob với điều kiện bạn không quá cầu kỳ về hiệu suất Linq mà thay vào đó là mã có thể duy trì ngắn gọn như tôi: trong trường hợp đó bạn có thể xóa MergeKind mặc định.SkipD repeatates để thực thi một sự lựa chọn cho người gọi và làm cho nhà phát triển nhận thức được kết quả sẽ như thế nào!


Câu trả lời được tinh chỉnh dưới đây mà không có enum nhị phân không cần thiết khi một bool sẽ làm ...
mattjs

2

Lưu ý rằng nếu bạn sử dụng phương thức tiện ích mở rộng có tên 'Thêm', bạn có thể sử dụng bộ khởi tạo bộ sưu tập để kết hợp nhiều từ điển khi cần như thế này:

public static void Add<K, V>(this Dictionary<K, V> d, Dictionary<K, V> other) {
  foreach (var kvp in other)
  {
    if (!d.ContainsKey(kvp.Key))
    {
      d.Add(kvp.Key, kvp.Value);
    }
  }
}


var s0 = new Dictionary<string, string> {
  { "A", "X"}
};
var s1 = new Dictionary<string, string> {
  { "A", "X" },
  { "B", "Y" }
};
// Combine as many dictionaries and key pairs as needed
var a = new Dictionary<string, string> {
  s0, s1, s0, s1, s1, { "C", "Z" }
};

1

Sáp nhập bằng phương pháp mở rộng. Nó không ném ngoại lệ khi có các khóa trùng lặp, nhưng thay thế các khóa đó bằng các khóa từ từ điển thứ hai.

internal static class DictionaryExtensions
{
    public static Dictionary<T1, T2> Merge<T1, T2>(this Dictionary<T1, T2> first, Dictionary<T1, T2> second)
    {
        if (first == null) throw new ArgumentNullException("first");
        if (second == null) throw new ArgumentNullException("second");

        var merged = new Dictionary<T1, T2>();
        first.ToList().ForEach(kv => merged[kv.Key] = kv.Value);
        second.ToList().ForEach(kv => merged[kv.Key] = kv.Value);

        return merged;
    }
}

Sử dụng:

Dictionary<string, string> merged = first.Merge(second);

1

Đơn giản hóa từ việc sử dụng so với câu trả lời trước đây của tôi với mặc định bool là hợp nhất không phá hủy nếu tồn tại hoặc ghi đè hoàn toàn nếu đúng thay vì sử dụng enum. Nó vẫn phù hợp với nhu cầu của riêng tôi mà không cần bất kỳ mã fancier nào:

using System.Collections.Generic;
using System.Linq;

public static partial class Extensions
{
    public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, bool overwrite = false)
    {
        source.ToList().ForEach(_ => {
            if ((!target.ContainsKey(_.Key)) || overwrite)
                target[_.Key] = _.Value;
        });
    }
}

0

Hợp nhất bằng cách sử dụng một EqualityComparerbản đồ để so sánh với một giá trị / loại khác. Ở đây chúng tôi sẽ ánh xạ từ KeyValuePair(loại mục khi liệt kê từ điển) đến Key.

public class MappedEqualityComparer<T,U> : EqualityComparer<T>
{
    Func<T,U> _map;

    public MappedEqualityComparer(Func<T,U> map)
    {
        _map = map;
    }

    public override bool Equals(T x, T y)
    {
        return EqualityComparer<U>.Default.Equals(_map(x), _map(y));
    }

    public override int GetHashCode(T obj)
    {
        return _map(obj).GetHashCode();
    }
}

Sử dụng:

// if dictA and dictB are of type Dictionary<int,string>
var dict = dictA.Concat(dictB)
                .Distinct(new MappedEqualityComparer<KeyValuePair<int,string>,int>(item => item.Key))
                .ToDictionary(item => item.Key, item=> item.Value);

0

hoặc là :

public static IDictionary<TKey, TValue> Merge<TKey, TValue>( IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
    {
        return x
            .Except(x.Join(y, z => z.Key, z => z.Key, (a, b) => a))
            .Concat(y)
            .ToDictionary(z => z.Key, z => z.Value);
    }

kết quả là một liên minh trong đó các mục trùng lặp "y" sẽ thắng.


0
public static IDictionary<K, V> AddRange<K, V>(this IDictionary<K, V> one, IDictionary<K, V> two)
        {
            foreach (var kvp in two)
            {
                if (one.ContainsKey(kvp.Key))
                    one[kvp.Key] = two[kvp.Key];
                else
                    one.Add(kvp.Key, kvp.Value);
            }
            return one;
        }

0

Một phiên bản từ @ user166390 trả lời với một IEqualityComparertham số được thêm vào để cho phép so sánh khóa không phân biệt chữ hoa chữ thường.

    public static T MergeLeft<T, K, V>(this T me, params Dictionary<K, V>[] others)
        where T : Dictionary<K, V>, new()
    {
        return me.MergeLeft(me.Comparer, others);
    }

    public static T MergeLeft<T, K, V>(this T me, IEqualityComparer<K> comparer, params Dictionary<K, V>[] others)
        where T : Dictionary<K, V>, new()
    {
        T newMap = Activator.CreateInstance(typeof(T), new object[] { comparer }) as T;

        foreach (Dictionary<K, V> src in 
            (new List<Dictionary<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;
    }

0
fromDic.ToList().ForEach(x =>
        {
            if (toDic.ContainsKey(x.Key))
                toDic.Remove(x.Key);
            toDic.Add(x);
        });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.