डिक्शनरी में AddRange क्यों नहीं है?


115

शीर्षक पर्याप्त बुनियादी है, मैं क्यों नहीं कर सकता:

Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());


2
वहाँ बहुत कुछ है कि AddRange नहीं है, जो हमेशा मुझे रहस्यमय है। संग्रह <> की तरह। बस हमेशा अजीब लगता था कि सूची <> में यह था, लेकिन संग्रह <> या अन्य IList और ICollection ऑब्जेक्ट नहीं।
टिम

39
मैं यहां एक एरिक लिपर्ट को खींचने जा रहा हूं: "क्योंकि उस सुविधा को किसी ने भी डिज़ाइन, निर्दिष्ट, कार्यान्वित, परीक्षण, प्रलेखित और शिप नहीं किया है।"
गाबे मुथर्ट

3
@ गैबी मुथरत - ठीक वही है जो मैंने ग्रहण किया था। मुझे दूसरे लोगों पर उस लाइन का उपयोग करना बहुत पसंद है। हालांकि वे इससे नफरत करते हैं। :)
टिम

2
@GabeMoothart यह कहना आसान नहीं होगा "क्योंकि आप नहीं कर सकते" या "क्योंकि यह" या "क्योंकि" नहीं है। - मैं अंदाजा लगा रहा हूं कि यह कहने के लिए उतना मजेदार नहीं है? --- मेरा अनुवर्ती सवाल आपके (मैं अनुमान लगा रहा हूं या पैराफ्रास्ड हूं) प्रतिक्रिया है "किसी ने कभी भी डिज़ाइन, निर्दिष्ट, लागू, परीक्षण, दस्तावेज़ और उस सुविधा को जहाज क्यों नहीं किया?", जिसके लिए आप बहुत अच्छी तरह से हो सकते हैं? "क्योंकि किसी ने नहीं किया" के साथ प्रतिक्रिया करने के लिए मजबूर किया गया, जो कि मेरे द्वारा पहले सुझाई गई प्रतिक्रियाओं के बराबर है। केवल दूसरा विकल्प जो मैं कल्पना कर सकता हूं वह व्यंग्यात्मक नहीं है और ओपी वास्तव में आश्चर्यचकित था।
कोड जॉकी

जवाबों:


76

मूल प्रश्न के लिए एक टिप्पणी यह ​​बहुत अच्छी तरह से बैठता है:

क्योंकि कोई भी उस सुविधा को डिज़ाइन, निर्दिष्ट, कार्यान्वित, परीक्षण, प्रलेखित और शिप नहीं किया गया है। - @ गाबा मुथरत

लेकिन क्यों? खैर, संभावना है क्योंकि शब्दकोशों को विलय करने का व्यवहार इस तरह से नहीं किया जा सकता है जो फ्रेमवर्क दिशानिर्देशों के साथ फिट बैठता है।

AddRangeमौजूद नहीं है क्योंकि एक सीमा का एक सहयोगी कंटेनर से कोई मतलब नहीं है, क्योंकि डेटा की सीमा डुप्लिकेट प्रविष्टियों के लिए अनुमति देती है। उदाहरण के लिए, यदि आपके पास IEnumerable<KeyValuePair<K,T>>वह संग्रह है जो डुप्लिकेट प्रविष्टियों के विरुद्ध रक्षा नहीं करता है।

कुंजी-मूल्य जोड़े के संग्रह को जोड़ने, या यहां तक ​​कि दो शब्दकोशों को मर्ज करने का व्यवहार सीधे-आगे है। हालांकि, कई डुप्लिकेट प्रविष्टियों से निपटने का तरीका नहीं है।

जब यह डुप्लिकेट के साथ व्यवहार करता है तो विधि का व्यवहार क्या होना चाहिए?

मेरे विचार से कम से कम तीन समाधान हो सकते हैं:

  1. पहली प्रविष्टि के लिए एक अपवाद फेंकें जो एक डुप्लिकेट है
  2. एक अपवाद को फेंक दें जिसमें सभी डुप्लिकेट प्रविष्टियाँ हैं
  3. डुप्लिकेट को अनदेखा करें

जब एक अपवाद को फेंक दिया जाता है, तो मूल शब्दकोश की स्थिति क्या होनी चाहिए?

Addलगभग हमेशा एक परमाणु ऑपरेशन के रूप में लागू किया जाता है: यह संग्रह की स्थिति को सफल और अपडेट करता है, या यह विफल रहता है, और संग्रह की स्थिति को अपरिवर्तित छोड़ दिया जाता है। जैसा कि AddRangeडुप्लिकेट त्रुटियों के कारण विफल हो सकता है, इसके व्यवहार को सुसंगत रखने का तरीका Addयह भी होगा कि किसी भी डुप्लिकेट पर एक अपवाद को फेंककर इसे परमाणु बना दिया जाए, और मूल शब्दकोश की स्थिति को अपरिवर्तित छोड़ दें।

एपीआई उपभोक्ता के रूप में, डुप्लिकेट तत्वों को पुनरावृत्त करने के लिए इसे थकाऊ AddRangeहोना चाहिए , जिसका अर्थ है कि एक एकल अपवाद को फेंकना चाहिए जिसमें सभी डुप्लिकेट मान शामिल हैं।

चुनाव तब उबलता है:

  1. सभी डुप्लिकेट के साथ एक अपवाद फेंक दें, मूल शब्दकोश को अकेला छोड़ दें।
  2. डुप्लिकेट को अनदेखा करें और आगे बढ़ें।

दोनों उपयोग मामलों का समर्थन करने के लिए तर्क हैं। ऐसा करने के लिए, क्या आप IgnoreDuplicatesहस्ताक्षर में एक झंडा जोड़ते हैं ?

IgnoreDuplicatesध्वज (जब सही पर सेट) भी अंतर्निहित कार्यान्वयन के रूप में, एक महत्वपूर्ण गति प्रदान करेगा होगा डुप्लिकेट जांच के लिए कोड बाईपास।

तो अब, आपके पास एक ध्वज है जो AddRangeदोनों मामलों का समर्थन करने की अनुमति देता है , लेकिन इसका एक अनिर्दिष्ट दुष्प्रभाव है (जो कि फ्रेमवर्क डिजाइनरों ने बचने के लिए बहुत कठिन काम किया है)।

सारांश

चूंकि डुप्लिकेट से निपटने के लिए कोई स्पष्ट, सुसंगत और अपेक्षित व्यवहार नहीं है, इसलिए उन सभी के साथ एक साथ सौदा नहीं करना आसान है, और शुरू करने के लिए विधि प्रदान नहीं करना है।

यदि आप खुद को लगातार शब्दकोशों को मर्ज करने के लिए पाते हैं, तो आप निश्चित रूप से शब्दकोशों को मर्ज करने के लिए अपनी खुद की विस्तार विधि लिख सकते हैं, जो आपके आवेदन के लिए काम करने वाले तरीके से व्यवहार करेगा।


37
पूरी तरह से गलत, एक शब्दकोश में एक AddRange (IEnumerable <KeyValuePair <K, T >> मान) होना चाहिए
Gusman

19
अगर इसमें ऐड हो सकता है, तो इसे कई ऐड करने में भी सक्षम होना चाहिए। जब आप डुप्लिकेट कुंजियों के साथ आइटम जोड़ने का प्रयास करते हैं तो व्यवहार उसी तरह होना चाहिए जब आप डुप्लिकेट कुंजी जोड़ते हैं।
यूरिया ब्लेथरविक

5
AddMultipleसे अलग है AddRange, भले ही यह लागू हो रहा है कि वह विजयी होने जा रहा है: क्या आप सभी डुप्लिकेट कुंजी की एक सरणी के साथ एक अपवाद फेंकते हैं ? या क्या आप पहली डुप्लिकेट कुंजी पर एक अपवाद फेंकते हैं जो आप मुठभेड़ करते हैं? यदि अपवाद को फेंक दिया जाए तो शब्दकोश की स्थिति क्या होनी चाहिए? प्रिस्टिन, या सभी चाबियाँ जो सफल रहीं?
एलन

3
ठीक है, इसलिए अब मुझे अपने असंख्य को मैन्युअल रूप से पुनरावृत्त करना होगा और उन्हें व्यक्तिगत रूप से जोड़ना होगा, जो आपके द्वारा उल्लिखित डुप्लिकेट के बारे में सभी कैविटीज़ के साथ होगा। फ्रेमवर्क से इसे कैसे छोड़ना कुछ भी हल करता है?
doug65536

4
@ doug65536 क्योंकि आप एपीआई उपभोक्ता के रूप में, अब यह तय कर सकते हैं कि आप प्रत्येक व्यक्ति के साथ क्या करना चाहते हैं Add- या तो प्रत्येक Addको एक में लपेटें try...catchऔर डुप्लिकेट को उस तरह से पकड़ें ; इंडेक्सर का उपयोग करें या बाद के मूल्य के साथ पहले मूल्य को अधिलेखित करें; या इस प्रकार मूल मूल्य को संरक्षित ContainsKeyकरने के लिए प्रयास करने से पहले पूर्व-जाँच करें Add। यदि फ्रेमवर्क AddRangeया एक AddMultipleविधि थी, तो संवाद करने का एकमात्र सरल तरीका जो हुआ था वह एक अपवाद के माध्यम से होगा, और इसमें शामिल हैंडलिंग और पुनर्प्राप्ति कोई कम जटिल नहीं होगी।
ज़ेव स्पिट्ज

36

मुझे कुछ समाधान मिला है:

Dictionary<string, string> mainDic = new Dictionary<string, string>() { 
    { "Key1", "Value1" },
    { "Key2", "Value2.1" },
};
Dictionary<string, string> additionalDic= new Dictionary<string, string>() { 
    { "Key2", "Value2.2" },
    { "Key3", "Value3" },
};
mainDic.AddRangeOverride(additionalDic); // Overrides all existing keys
// or
mainDic.AddRangeNewOnly(additionalDic); // Adds new keys only
// or
mainDic.AddRange(additionalDic); // Throws an error if keys already exist
// or
if (!mainDic.ContainsKeys(additionalDic.Keys)) // Checks if keys don't exist
{
    mainDic.AddRange(additionalDic);
}

...

namespace MyProject.Helper
{
  public static class CollectionHelper
  {
    public static void AddRangeOverride<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
    {
        dicToAdd.ForEach(x => dic[x.Key] = x.Value);
    }

    public static void AddRangeNewOnly<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
    {
        dicToAdd.ForEach(x => { if (!dic.ContainsKey(x.Key)) dic.Add(x.Key, x.Value); });
    }

    public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
    {
        dicToAdd.ForEach(x => dic.Add(x.Key, x.Value));
    }

    public static bool ContainsKeys<TKey, TValue>(this IDictionary<TKey, TValue> dic, IEnumerable<TKey> keys)
    {
        bool result = false;
        keys.ForEachOrBreak((x) => { result = dic.ContainsKey(x); return result; });
        return result;
    }

    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
            action(item);
    }

    public static void ForEachOrBreak<T>(this IEnumerable<T> source, Func<T, bool> func)
    {
        foreach (var item in source)
        {
            bool result = func(item);
            if (result) break;
        }
    }
  }
}

मज़े करो।


आप की जरूरत नहीं है ToList(), एक शब्दकोश एक है IEnumerable<KeyValuePair<TKey,TValue>। साथ ही, यदि आप किसी मौजूदा कुंजी मान को जोड़ते हैं, तो दूसरी और तीसरी विधि विधियाँ फेंकेगी। एक अच्छा विचार नहीं है, क्या आप ढूंढ रहे थे TryAdd? अंत में, दूसरे को बदल दिया जा सकता हैWhere(pair->!dic.ContainsKey(pair.Key)...
पानीगोटिस कानवास

2
ठीक है, ToList()अच्छा समाधान नहीं है इसलिए मैंने कोड बदल दिया है। try { mainDic.AddRange(addDic); } catch { do something }यदि आप तीसरी विधि के लिए सुनिश्चित नहीं हैं, तो आप इसका उपयोग कर सकते हैं । दूसरी विधि पूरी तरह से काम करती है।
ADM-IT

हाय पनियागोटिस कानावोस, मुझे आशा है कि आप खुश होंगे।
ADM-IT

1
धन्यवाद, मैंने यह चुरा लिया।
मेटाबड्डी

14

यदि कोई व्यक्ति स्वयं की तरह इस प्रश्न पर आता है - IEnumerable एक्सटेंशन विधियों का उपयोग करके "AddRange" को प्राप्त करना संभव है:

var combined =
    dict1.Union(dict2)
        .GroupBy(kvp => kvp.Key)
        .Select(grp => grp.First())
        .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

शब्दकोशों के संयोजन के दौरान मुख्य चाल डुप्लिकेट कुंजी के साथ काम कर रही है। ऊपर के कोड में यह हिस्सा है .Select(grp => grp.First())। इस मामले में यह केवल डुप्लिकेट के समूह से पहला तत्व लेता है, लेकिन यदि आवश्यक हो तो आप अधिक परिष्कृत तर्क को लागू कर सकते हैं।


क्या होगा यदि डिफ़ॉल्ट समानता तुलनित्र का उपयोग dict1 नहीं करता है ?
mjwills

Linq विधियाँ आपको आइकक्विटीकम्पेरर में पास करने देती हैं जब प्रासंगिक:var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
काइल मैकलेलन

12

मेरा अनुमान उपयोगकर्ता को उचित आउटपुट का अभाव है जैसा कि हुआ था। जैसा कि आप एक शब्दकोश में कुंजियों को दोहरा नहीं सकते हैं, तो आप दो शब्दकोशों को मर्ज करने से कैसे निपटेंगे जहां कुछ कुंजियाँ मिलती हैं? सुनिश्चित करें कि आप कह सकते हैं: "मुझे परवाह नहीं है" लेकिन यह झूठे / वापसी की परंपरा को तोड़ रहा है और चाबियों को दोहराने के लिए एक अपवाद फेंक रहा है।


5
यह कैसे अलग है जहां से आप कॉल करते समय की-क्लैश करते हैं Add, इसके अलावा यह एक से अधिक बार भी हो सकता है। यह वही करता है ArgumentExceptionजो Addनिश्चित रूप से करता है?
निकोडेमस 13

1
@ nicodemus13 हाँ, लेकिन आपको यह नहीं पता होगा कि किस कुंजी ने अपवाद को फेंक दिया, केवल यह कि कुछ कुंजी दोहराई गई थी।
गल

1
@ गाल प्रदान की गई, लेकिन आप कर सकते हैं: अपवाद संदेश में परस्पर विरोधी कुंजी का नाम लौटाएं (किसी के लिए उपयोगी जो जानता है कि वे क्या कर रहे हैं, मुझे लगता है ...), या आप इसे (के हिस्से के रूप में) डाल सकते हैं? आपके द्वारा फेंके गए तर्क, या-आप के लिए तर्क का विरोधाभास, आप एक नया अपवाद प्रकार बना सकते हैं (शायद एक सामान्य पर्याप्त विकल्प हो सकता है NamedElementException??), या तो इसके बजाय फेंक दिया या एक आर्ग्यूमेंट अपवाद के आंतरिक अपवाद के रूप में, जो नामित तत्व को निर्दिष्ट करता है जो संघर्ष करता है ... कुछ अलग विकल्प, मैं कहूंगा
कोड जॉकी

7

आप ऐसा कर सकते हैं

Dictionary<string, string> dic = new Dictionary<string, string>();
// dictionary other items already added.
MethodThatReturnAnotherDic(dic);

public void MethodThatReturnAnotherDic(Dictionary<string, string> dic)
{
    dic.Add(.., ..);
}

या ऊपर के पैटर्न को जोड़ने और / या उपयोग करने के लिए एक सूची का उपयोग करें।

List<KeyValuePair<string, string>>

1
शब्दकोश में पहले से ही एक रचनाकार है जो किसी अन्य शब्दकोश को स्वीकार करता है
पानागोटीस कानावोस

1
ओपी व्यवस्था करना चाहता है, एक शब्दकोश क्लोन करने के लिए नहीं। मेरे उदाहरण में विधि के नाम के रूप में MethodThatReturnAnotherDic। यह ओपी से आता है। कृपया प्रश्न और मेरे उत्तर की फिर से समीक्षा करें।
वलमास

1

यदि आप w / एक नया शब्दकोश काम कर रहे हैं (और आपके पास खोने के लिए मौजूदा पंक्तियाँ नहीं हैं), तो आप हमेशा ऑब्जेक्ट्स की एक अन्य सूची से To मंदार () का उपयोग कर सकते हैं।

तो, आपके मामले में, आप कुछ इस तरह से करेंगे:

Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);

5
आपको एक नया शब्दकोश बनाने की भी आवश्यकता नहीं है, बस लिखेंDictionary<string, string> dic = SomeList.ToDictionary...
पनगीओटीस कानावोस

1

यदि आप जानते हैं कि आपके पास डुप्लिकेट कुंजियाँ नहीं हैं, तो आप कर सकते हैं:

dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

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

मुझे नहीं पता कि यह रूपरेखा में क्यों नहीं है; होना चाहिए। कोई अनिश्चितता नहीं है; बस एक अपवाद फेंक दें। इस कोड के मामले में, यह एक अपवाद नहीं है।


यदि मूल शब्दकोश का उपयोग करके बनाया गया था तो क्या होगा var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);?
2:24 बजे

1

इस तरह से विस्तार विधि का उपयोग करने के लिए स्वतंत्र महसूस करें:

public static Dictionary<T, U> AddRange<T, U>(this Dictionary<T, U> destination, Dictionary<T, U> source)
{
  if (destination == null) destination = new Dictionary<T, U>();
  foreach (var e in source)
    destination.Add(e.Key, e.Value);
  return destination;
}

0

यहाँ c # 7 ValueTuples (tuple literals) का उपयोग कर एक वैकल्पिक समाधान दिया गया है

public static class DictionaryExtensions
{
    public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> source,  IEnumerable<ValueTuple<TKey, TValue>> kvps)
    {
        foreach (var kvp in kvps)
            source.Add(kvp.Item1, kvp.Item2);

        return source;
    }

    public static void AddTo<TKey, TValue>(this IEnumerable<ValueTuple<TKey, TValue>> source, Dictionary<TKey, TValue> target)
    {
        target.AddRange(source);
    }
}

जैसे इस्तेमाल किया

segments
    .Zip(values, (s, v) => (s.AsSpan().StartsWith("{") ? s.Trim('{', '}') : null, v))
    .Where(zip => zip.Item1 != null)
    .AddTo(queryParams);

0

जैसा कि दूसरों ने उल्लेख किया है, इसका कारण यह Dictionary<TKey,TVal>.AddRangeहै कि लागू नहीं किया गया है क्योंकि ऐसे कई तरीके हैं जिनसे आप उन मामलों को संभालना चाहते हैं जहां आपके पास डुप्लिकेट हैं। यह भी बात लागू होती है Collectionया इस तरह के रूप इंटरफेस IDictionary<TKey,TVal>, ICollection<T>आदि

केवल List<T>इसे लागू करता है, और आप ध्यान देंगे कि IList<T>इंटरफ़ेस समान कारणों से नहीं करता है: एक संग्रह में मूल्यों की एक सीमा को जोड़ने पर अपेक्षित व्यवहार संदर्भ के आधार पर मोटे तौर पर भिन्न हो सकते हैं।

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

MethodThatReturnAnotherDic().ToList.ForEach(kvp => dic.Add(kvp.Key, kvp.Value));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.