C # में शब्दकोश मर्ज करना


493

Dictionary<T1,T2>C # में 2 या अधिक शब्दकोशों ( ) को मर्ज करने का सबसे अच्छा तरीका क्या है ? (LINQ जैसी 3.0 विशेषताएँ ठीक हैं)।

मैं एक विधि हस्ताक्षर की तर्ज पर सोच रहा हूँ:

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

या

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

संपादित करें: JaredPar और जॉन स्कीट से एक शांत समाधान मिला, लेकिन मैं कुछ ऐसा सोच रहा था जो डुप्लिकेट कुंजियों को संभालता है। टकराव के मामले में, यह कोई फर्क नहीं पड़ता कि कौन सा मान तब तक सहेजा जाता है जब तक यह सुसंगत न हो।


174
असंबंधित, लेकिन डुप्लिकेट कुंजी जांच के बिना सिर्फ दो शब्दकोशों को मर्ज करने की तलाश में किसी के लिए, यह अच्छी तरह से काम करता है:dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
बेंजोल

6
@ बैंजोल आप इसे उत्तर अनुभाग में जोड़ सकते थे
एम। कुमारन

4
क्लोजर का सी # में विलय:dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
ब्रूस

@ बैंजोल: पहले शब्दकोश से प्रविष्टियों को प्राथमिकता देकर डुप्लिकेट को समाप्त करें dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value)
सनकैट २२

जवाबों:


320

यह आंशिक रूप से इस बात पर निर्भर करता है कि यदि आप डुप्लिकेट में भाग लेते हैं तो आप क्या करना चाहते हैं। उदाहरण के लिए, आप कर सकते हैं:

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

यदि आपको कोई डुप्लिकेट कुंजी मिलती है तो वह एक अपवाद को फेंक देगा।

संपादित करें: यदि आप ToLookup का उपयोग करते हैं तो आपको एक लुकअप मिलेगा जिसमें प्रति कुंजी कई मान हो सकते हैं। फिर आप इसे एक शब्दकोश में बदल सकते हैं :

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

यह थोड़ा बदसूरत है - और अक्षम - लेकिन यह कोड के संदर्भ में ऐसा करने का सबसे तेज़ तरीका है। (मैंने इसका परीक्षण नहीं किया है, स्वीकार किया है।)

आप निश्चित रूप से अपना खुद का To मंद 2 एक्सटेंशन विधि लिख सकते हैं (एक बेहतर नाम के साथ, लेकिन मेरे पास अब एक सोचने का समय नहीं है) - यह करना बहुत मुश्किल नहीं है, बस डुप्लिकेट कुंजियों को ओवरराइट करना (या अनदेखा करना)। महत्वपूर्ण बिट (मेरे दिमाग में) SelectMany का उपयोग कर रहा है, और यह महसूस करता है कि एक शब्दकोश इसकी कुंजी / मूल्य जोड़े पर पुनरावृत्ति का समर्थन करता है।


3
वास्तव में मूल्यों को मर्ज करने के बजाय उन्हें केवल पहले शब्दकोष से लें आप समूह => समूह (रिप्ले) को समूह => समूह के साथ बदल सकते हैं। जॉन स्केट के संपादित उत्तर में समूह (मूल्य => मूल्य)।
andreialecu

3
अब मैं सोच रहा हूँ कि GroupBy ToLookup से बेहतर अनुकूल होगा? मुझे लगता है कि ILookup सिर्फ एक अनुक्रमणिका, आकार संपत्ति जोड़ता है और IGrouping के शीर्ष पर विधि शामिल है - तो यह एक छोटे से अधिक तेजी से हो गया?
Toong

2
@toong: या तो ठीक होना चाहिए, ईमानदार होना चाहिए। उपयोग करना GroupByवास्तव में थोड़ा अधिक कुशल हो सकता है - लेकिन मुझे संदेह है कि यह किसी भी तरह से महत्वपूर्ण होगा।
जॉन स्कीट

1
@ जो: यह ToDictionaryविधि कॉल करने के लिए तुलना प्रदान करके काफी सरल है । मैं हालांकि अधिक सामान्य मामले के लिए उत्तर को सरल रखना पसंद करूंगा, और मुझे उम्मीद है कि जिस किसी को भी कस्टम तुलनित्र की आवश्यकता होगी, वह प्रासंगिक अधिभार को खोजने में सक्षम है।
जॉन स्कीट

1
@ सर्ज: मेरा सुझाव है कि आप सटीक आवश्यकताओं के साथ एक नया प्रश्न पूछें।
जॉन स्कीट

264

मैं इसे इस तरह से करूंगा:

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

सरल और आसान। इस ब्लॉग पोस्ट के अनुसार यह सबसे अधिक लूप से भी तेज है क्योंकि इसका अंतर्निहित कार्यान्वयन एन्यूमरेटर के बजाय इंडेक्स द्वारा तत्वों तक पहुंचता है (इस उत्तर को देखें)

यह निश्चित रूप से एक अपवाद को फेंक देगा यदि डुप्लिकेट हैं, तो आपको विलय करने से पहले जांचना होगा।


169
अगर डुप्लिकेट बात है, का उपयोग करें dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value)। इस तरह से मान dictionaryFromसंभवतया मौजूदा कुंजी के लिए मान को ओवरराइड करता है।
okrumnow

7
यह एक लूप से तेज होने की संभावना नहीं है - आप भूल जाते हैं कि ToList () वास्तव में शब्दकोश की नकल करता है। कोड हालांकि तेज़! ;-)
सोरेन बोइसन

6
यह तेज़ है .. क्योंकि टॉलिस्ट () वास्तव में शब्दकोश की नकल करने से बाज नहीं आता है, यह सिर्फ उनके अंदर के तत्वों के संदर्भ की प्रतिलिपि बनाता है। मूल रूप से सूची ऑब्जेक्ट में ही स्मृति में एक नया पता होगा, ऑब्जेक्ट समान हैं !!
गुरू

4
ब्लॉग पोस्ट गायब हो गई लेकिन याय वेकबैक मशीन: web.archive.org/web/20150311191313/http://diditwith.net/2006/10/……
CAD bloke

1
हम्म, की तुलना में बेहतर जॉन स्कीट का जवाब, मुझे लगता है।
SAN16ошƒаӽ

102

यह पता नहीं चलता है कि क्या कई कुंजियाँ हैं ("राइटर" कुंजी "लेफ्टर" कीज़ को प्रतिस्थापित करती है), कई शब्दकोशों (यदि वांछित हो) को मर्ज कर सकती है और प्रकार को संरक्षित कर सकती है (प्रतिबंध के साथ कि उसे एक सार्थक डिफ़ॉल्ट सार्वजनिक निर्माता की आवश्यकता होती है):

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

}

बहुत बढ़िया। यह वही था जो मुझे चाहिए था। जेनरिक का अच्छा उपयोग। सहमत हैं कि वाक्यविन्यास थोड़ा अजीब है, लेकिन आप इसके बारे में कुछ नहीं कर सकते।
पीटर एम।

2
मुझे यह समाधान पसंद आया, लेकिन एक चेतावनी है: यदि this T meशब्दकोश का उपयोग करके new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)या कुछ इसी तरह की घोषणा की गई थी , तो परिणामी शब्दकोश समान व्यवहार को बनाए नहीं रखेगा।
जोहो पोर्टेला

3
यदि आप meएक बनाते Dictionary<K,V>हैं var newMap = new Dictionary<TKey, TValue>(me, me.Comparer);, तो आप कर सकते हैं , और आप अतिरिक्त Tप्रकार के पैरामीटर से भी बचते हैं ।
एनेव्स

1
क्या किसी के पास इस जानवर का उपयोग करने का एक उदाहरण है?
टिम

1
@ समय: मैंने नीचे दिए गए जानवर के लिए एक उदाहरण जोड़ा, मैंने एनीव्स द्वारा समाधान जोड़ा और परिणाम एक अधिक पालतू जानवर है :)
केनी

50

तुच्छ समाधान होगा:

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

निम्नलिखित प्रयास करें

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
बस सोच रहा था - क्या सिर्फ यूनियन सिर्फ काम नहीं करेगी ? foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))उत्पादन कोड में इसका उपयोग, अगर कोई डाउनसाइड हैं तो कृपया मुझे बताएं! :)
मार्स रॉबर्टसन

1
@ मिचल: यूनियन वह स्थान लौटेगा IEnumerable<KeyValuePair<TKey, TValue>>जहां समानता और मूल्य की समानता द्वारा परिभाषित किया गया है (वैकल्पिक विकल्प की अनुमति देने के लिए एक अधिभार है)। यह फॉरच लूप के लिए ठीक है, जहां यह सब आवश्यक है, लेकिन ऐसे मामले हैं जहां आप वास्तव में शब्दकोश चाहते हैं।
तीमुथियुस

15

मुझे पार्टी में बहुत देर हो चुकी है और शायद कुछ याद आ रहा है, लेकिन अगर या तो कोई डुप्लिकेट कुंजी नहीं है या, जैसा कि ओपी कहता है, "टकराव के मामले में, इससे कोई फर्क नहीं पड़ता कि कौन से मूल्य को तब तक बचाया जाता है सुसंगत, "इस एक के साथ क्या गलत है (डी 2 को डी 1 में विलय करना)?

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

यह काफी सरल लगता है, शायद बहुत सरल है, मुझे आश्चर्य है कि अगर मैं कुछ याद कर रहा हूं। यह वही है जो मैं कुछ कोड में उपयोग कर रहा हूं जहां मुझे पता है कि कोई डुप्लिकेट कुंजी नहीं है। मैं अभी भी परीक्षण में हूँ, इसलिए, मुझे यह जानकर अच्छा लगेगा कि अगर मैं बाद में पता लगाने के बजाय किसी चीज़ की अनदेखी कर रहा हूँ।


4
सबसे साफ और सबसे पठनीय समाधान imo में से एक और ToList () से बचा जाता है जो कई अन्य उपयोग करते हैं।
404

15

निम्नलिखित मेरे लिए काम करता है। यदि डुप्लिकेट हैं, तो यह डिक्टा के मूल्य का उपयोग करेगा।

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 मुझे नहीं लगता कि मैंने इसका सामना किया था जब मैंने इसका उत्तर दिया था (नया C # देव)
एतान रीसोर

मुझे 'जहां TValue: class' को एक मान के रूप में
मार्गदर्शिका

@ om471987 हां, गाइड एक ऐसी संरचना है जो एक वर्ग नहीं है, इसलिए यह समझ में आता है। मुझे लगता है कि मूल रूप से मुझे कुछ समस्या थी जब मैंने उस खंड को नहीं जोड़ा था। अब और क्यों याद नहीं।
एथन रीसर

10

यहाँ एक सहायक कार्य है जिसका मैं उपयोग करता हूँ:

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

यहां बहुत सारे महान समाधान सूचीबद्ध हैं, लेकिन मुझे यह सबसे सरलता पसंद है। बस मेरी व्यक्तिगत प्राथमिकता।
जेसन

3
if(first == null)तब यह तर्क बेकार है क्योंकि वापस firstनहीं है refऔर वापस नहीं किया गया है। और यह नहीं हो सकता refक्योंकि इसे एक इंटरफ़ेस उदाहरण के रूप में घोषित किया गया है; आपको या तो त्रुटि-जाँच को हटाने या इसे एक श्रेणी उदाहरण के रूप में घोषित करने की आवश्यकता है या बस एक नया शब्दकोश (कोई विस्तार विधि के साथ) वापस करें।
ग्रेल्ट

@Jesdisciple - अपने सुझाव के अनुसार मुद्दे को हटा दिया
एंड्रयू हैरी

8

विकल्प 1: यह इस बात पर निर्भर करता है कि आप क्या करना चाहते हैं यदि आप सुनिश्चित हैं कि आपके पास दोनों शब्दकोशों में डुप्लिकेट कुंजी नहीं है। आप जितना कर सकते थे:

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

नोट: यदि आपको कोई डुप्लिकेट कुंजी शब्दकोशों में मिलती है, तो यह त्रुटि को फेंक देगा।

विकल्प 2: यदि आपके पास डुप्लिकेट कुंजी हो सकती है तो आपको क्लॉज़ के उपयोग के साथ डुप्लिकेट कुंजी को संभालना होगा।

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

नोट: इसमें डुप्लिकेट कुंजी नहीं मिलेगी। अगर कोई डुप्लिकेट कुंजी होगी तो उसे शब्दकोश 1 की कुंजी मिलेगी।

विकल्प 3: यदि आप ToLookup का उपयोग करना चाहते हैं। तब आपको एक लुकअप मिलेगा जिसमें प्रति कुंजी कई मान हो सकते हैं। आप उस लुकअप को डिक्शनरी में बदल सकते हैं:

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

6

कैसे एक paramsअधिभार जोड़ने के बारे में ?

इसके अलावा, आपको उन्हें IDictionaryअधिकतम लचीलेपन के लिए टाइप करना चाहिए ।

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
वह यहाँ अन्य उत्तरों को जोड़ने की तरह है, जिन्हें ध्यान में रखते हुए आप DictionaryExtensions क्लास को भी अच्छा बना सकते हैं। शायद यह सवाल विकी बन जाए, या किसी वर्ग की जाँच के लिए ...
बजे क्रिस मोसची

5

शब्दकोश कुंजी के लुकअप और डिलीट के प्रदर्शन को ध्यान में रखते हुए, क्योंकि वे हैश ऑपरेशन हैं, और प्रश्न के शब्दों पर विचार करना सबसे अच्छा तरीका था , मुझे लगता है कि नीचे एक पूरी तरह से मान्य दृष्टिकोण है, और अन्य थोड़ा अधिक जटिल हैं, आईएमएचओ।

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

या यदि आप एक मल्टीथ्रेड एप्लिकेशन में काम कर रहे हैं और आपके शब्दकोश को वैसे भी सुरक्षित होना चाहिए, तो आपको ऐसा करना चाहिए:

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

आप इसे तब ले सकते हैं, ताकि इसे शब्दकोशों का एक संग्रह बना सकें। भले ही, आप दृश्यों के पीछे .Add()एक अतिरिक्त, अनावश्यक लेकिन व्यावहारिक रूप से मुक्त होने के बाद से, ~ O (3n) (सभी स्थितियों के सही होने) के बारे में देख रहे हैं Contains()। मुझे नहीं लगता कि यह बहुत बेहतर हो जाता है।

यदि आप बड़े संग्रहों पर अतिरिक्त संचालन को सीमित करना चाहते हैं, तो आपको Countप्रत्येक ऐसे शब्दकोश का योग करना चाहिए, जिसे आप विलय करने वाले हैं और लक्ष्य शब्दकोश की क्षमता को उस पर सेट करते हैं, जो आकार बदलने की बाद की लागत से बचा जाता है। तो, अंत उत्पाद कुछ इस तरह है ...

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

ध्यान दें कि मैंने IList<T>इस पद्धति में लिया ... अधिकतर क्योंकि यदि आप में लेते हैं IEnumerable<T>, तो आपने अपने आप को एक ही सेट के कई एन्यूमरेशन्स तक खोल दिया है, जो बहुत ही महंगा हो सकता है यदि आपको डिफ्रेंटेड LINQ से डिक्शनरी का कलेक्शन मिल गया है बयान।


4

उपरोक्त उत्तरों के आधार पर, लेकिन कॉलर को डुप्लिकेट को संभालने देने के लिए एक फंक-पैरामीटर जोड़ रहा है:

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

अब पार्टी बहुत सुंदर हो चुकी है, लेकिन यहाँ उपयोगकर्ता 16166390 का "बेहतर" संस्करण है, जिसने मेरे विस्तार पुस्तकालय में अपना स्थान बना लिया है। कुछ विवरणों के अलावा, मैंने विलय किए गए मूल्य की गणना करने के लिए एक प्रतिनिधि जोड़ा।

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

@ समय: टिप्पणी होनी चाहिए, लेकिन टिप्पणियाँ कोड संपादन की अनुमति नहीं देती हैं।

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

नोट: मैंने @Andrew Orsich द्वारा समाधान के लिए @ANeves से संशोधन लागू किया, इसलिए मर्जलेफ़्ट अब इस तरह दिखता है:

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

जो मैंने खुद लिखा, उसके करीब। यह, inc, केवल Dictionaryप्रकारों के लिए काम करेगा । अन्य शब्दकोश प्रकारों ( ConcurrentDictionary, ReadOnlyDictionaryइत्यादि) के लिए अधिभार जोड़ा जा सकता है , मुझे नहीं लगता कि एक नई सूची का निर्माण, और व्यक्तिगत रूप से कॉन्कैट आवश्यक है। मैंने पहले तो परम सरणी की पुनरावृति की, फिर प्रत्येक शब्दकोश के प्रत्येक KVP को पुन: प्रसारित किया। मुझे कोई ड्रा बैक नहीं दिख रहा है।
कुचलें

आप डिक्शनरी के बजाय
IDEDIA

Dictionaryयदि आप एक्सटेंशन पद्धति का उपयोग करते हैं तो भी आपको हमेशा एक वस्तु मिलती रहेगी ConcurrentDictionary। जिससे कीड़ों का पता लगाना मुश्किल हो सकता है।
मार्सेल

2

मुझे पता है कि यह एक पुराना प्रश्न है, लेकिन चूंकि अब हमारे पास LINQ है, आप इसे इस तरह से एक ही लाइन में कर सकते हैं

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

या

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

डाउनवोट @Cruces के लिए क्षमा करें, लेकिन आपका पहला उदाहरण डंप करता है उत्तर stackoverflow.com/a/6695211/704808 और आपका दूसरा उदाहरण केवल विस्तारित आईएनम्यूएरेबल <KeyValuePair "स्ट्रिंग, स्ट्रिंग" को छोड़ देता है, जिसमें से प्रत्येक आइटम को जोड़ने के बाद mergee
वियर

2

जटिल उत्तरों को देखकर डर गए, C # के लिए नए हैं।

यहाँ कुछ सरल उत्तर दिए गए हैं।
D1, d2, और इसी तरह विलय करना। किसी भी अतिव्यापन कुंजी को संभालें और (नीचे उदाहरण में "b"):

उदाहरण 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
}

उदाहरण 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
}

अधिक जटिल परिदृश्यों के लिए, अन्य उत्तर देखें।
उम्मीद है कि मदद की।


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

आप या तो छोड़ सकते हैं / अनदेखा कर सकते हैं (डिफ़ॉल्ट) या डुप्लिकेट को अधिलेखित कर सकते हैं: और बॉब के आपके चाचा ने आपको बशर्ते कि आप Linq प्रदर्शन के बारे में अधिक उधम मचाते नहीं हैं, लेकिन इसके बजाय बनाए रखने योग्य कोड को पसंद करते हैं जैसा कि मैं करता हूं: जिस स्थिति में आप डिफ़ॉल्ट MerkKind.SkipDuplicates को हटा सकते हैं लागू करने के लिए फोन करने वाले के लिए एक विकल्प है और डेवलपर को इस बात का संज्ञान लें कि परिणाम क्या होगा!


जब एक बूल क्या करेगा अनावश्यक बाइनरी
एनम के

2

ध्यान दें कि यदि आप 'Add' नामक एक एक्सटेंशन विधि का उपयोग करते हैं, तो आपको इस तरह के आवश्यकतानुसार कई शब्दकोशों को संयोजित करने के लिए संग्रह आरंभीकरण का उपयोग करने के लिए मिलता है:

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

एक्सटेंशन विधि का उपयोग करके विलय करना। डुप्लिकेट कुंजियों के होने पर यह अपवाद नहीं फेंकता है, लेकिन उन कुंजियों को दूसरे शब्दकोश से कुंजियों के साथ बदल देता है।

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

उपयोग:

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

1

गैर-विनाशकारी मर्ज के एक बूल डिफॉल्ट के साथ मेरे पहले के उत्तर की तुलना में उपयोग से सरलीकृत यदि मौजूदा या पूरी तरह से अधिलेखित हो जाता है, तो एनम का उपयोग करने के बजाय। यह अभी भी किसी भी कट्टरपंथी कोड की आवश्यकता के बिना मेरी खुद की आवश्यकताओं के अनुरूप है:

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

EqualityComparerकिसी भिन्न मान / प्रकार की तुलना के लिए उस मानचित्र आइटम का उपयोग करके विलय करना । यहाँ हम KeyValuePair(एक शब्दकोष की गणना करते समय आइटम प्रकार) से नक्शा करेंगे 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();
    }
}

उपयोग:

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

या:

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

परिणाम एक संघ है जहां डुप्लिकेट प्रविष्टियों के लिए "y" जीतता है।


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

IEqualityComparerअसंवेदनशील कुंजी तुलना के लिए अनुमति देने के लिए एक जोड़ा पैरामीटर के साथ @ user166390 उत्तर से एक संस्करण ।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.