क्या कोई IDEDIA कार्यान्वयन है जो कि गुम होने पर, फेंकने के बजाय डिफ़ॉल्ट मान लौटाता है?


129

शब्दकोश में अनुक्रमणिका एक अपवाद फेंकता है यदि कुंजी गायब है। क्या कोई IDEDIA का कार्यान्वयन है जो इसके बजाय डिफ़ॉल्ट (T) पर लौटेगा?

मैं "TryGetValue" विधि के बारे में जानता हूं, लेकिन यह linq के साथ उपयोग करना असंभव है।

क्या यह कुशलता से मुझे चाहिए? "

myDict.FirstOrDefault(a => a.Key == someKeyKalue);

मुझे नहीं लगता कि जैसा कि मुझे लगता है कि यह हैश लुकअप का उपयोग करने के बजाय कुंजियों को पुनरावृत्त करेगा।


10
बाद में इस प्रश्न को भी देखें, इस एक के डुप्लिकेट के रूप में चिह्नित, लेकिन विभिन्न उत्तरों के साथ: शब्दकोश एक डिफ़ॉल्ट मान लौटाता है यदि कुंजी मौजूद नहीं है
रोरी ओ'केन

2
@dylan अनुपलब्ध कुंजी पर एक अपवाद फेंक देगा, शून्य नहीं। इसके अलावा यह तय करने की आवश्यकता होगी कि डिक्शनरी का उपयोग हर जगह क्या होना चाहिए। इस प्रश्न की उम्र पर भी ध्यान दें। हमारे पास नहीं था ?? ऑपरेटर 8 साल पहले किसी भी तरह
TheSoftwareJedi

जवाबों:


147

वास्तव में, यह कुशल नहीं होगा।

आप हमेशा एक एक्सटेंशन विधि लिख सकते हैं:

public static TValue GetValueOrDefault<TKey,TValue>
    (this IDictionary<TKey, TValue> dictionary, TKey key)
{
    TValue ret;
    // Ignore return value
    dictionary.TryGetValue(key, out ret);
    return ret;
}

या C # 7.1 के साथ:

public static TValue GetValueOrDefault<TKey,TValue>
    (this IDictionary<TKey, TValue> dictionary, TKey key) =>
    dictionary.TryGetValue(key, out var ret) ? ret : default;

वह उपयोग करता है:

  • एक अभिव्यक्ति विधि (C # 6)
  • एक परिवर्तनशील चर (C # 7.0)
  • एक डिफ़ॉल्ट शाब्दिक (C # 7.1)

2
या अधिक कॉम्पैक्ट रूप से:return (dictionary.ContainsKey(key)) ? dictionary[key] : default(TValue);
पीटर ग्लुक

33
@PeterGluck: अधिक कॉम्पैक्ट रूप से, लेकिन कम कुशलता से ... कुंजी मौजूद होने पर मामले में दो लुक-अप क्यों करें?
जॉन स्कीट

5
@JonSkeet: पीटर को सही करने के लिए धन्यवाद; मैं उस "कम कुशल" विधि का उपयोग अब बिना कुछ समय के लिए कर रहा हूँ।
जे ब्रायन मूल्य

5
बहुत अच्छा! मैं बेशर्मी से plagiarize जा रहा हूँ - एर, यह कांटा। ;)
जॉन Coombs

3
जाहिरा तौर पर एमएस ने फैसला किया कि यह जोड़ने के लिए काफी अच्छा था System.Collections.Generic.CollectionExtensions, जैसा कि मैंने अभी कोशिश की है और यह वहां है।
TheMayer

19

इन विस्तार विधियों को ले जाने से मदद मिल सकती है।

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key)
{
    return dict.GetValueOrDefault(key, default(V));
}

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, V defVal)
{
    return dict.GetValueOrDefault(key, () => defVal);
}

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, Func<V> defValSelector)
{
    V value;
    return dict.TryGetValue(key, out value) ? value : defValSelector();
}

3
आखिरी ओवरलोड दिलचस्प है। चूंकि चयन करने के लिए कुछ भी नहीं है से , यह केवल आलसी मूल्यांकन का एक रूप है?
मेमार्क

1
@Markark हाँ मूल्य चयनकर्ता केवल जरूरत पड़ने पर चलाया जाता है, इसलिए एक तरह से इसे आलसी मूल्यांकन कहा जा सकता है, लेकिन इसके बारे में लिनक संदर्भ में अमल नहीं हुआ। लेकिन यहाँ आलसी मूल्यांकन कारक से चयन करने के लिए कुछ भी नहीं पर निर्भर है, आप निश्चित रूप से चयनकर्ता बना सकते हैं Func<K, V>
नवफाल

1
क्या वह तीसरी विधि सार्वजनिक है क्योंकि यह अपने आप में उपयोगी है? यही है, क्या आप सोच रहे हैं कि कोई व्यक्ति लैम्बडा में पास हो सकता है जो वर्तमान समय का उपयोग करता है, या ... hmm पर आधारित गणना। मुझे उम्मीद है कि सिर्फ दूसरी विधि में V मूल्य होगा; वापसी। ताना.टेटवैल्यू (कुंजी, मूल्य बाहर)? मान: defVal;
जॉन कोम्ब्स

1
@JCbs सही, तीसरा ओवरलोड एक ही उद्देश्य के लिए है। और हां बेशक आप ऐसा दूसरे ओवरलोड में कर सकते हैं, लेकिन मैं इसे थोड़ा सूखा बना रहा था (ताकि मैं तर्क न दोहराऊं)।
नवाफाल

4
@ नवाफ गुड पॉइंट। एक समय पर विधि कॉल से बचने की तुलना में सूखी रहना अधिक महत्वपूर्ण है।
जॉन Coombs

19

यदि कोई .net कोर 2 और इसके बाद के संस्करण (C # 7.X) का उपयोग कर रहा है , तो CollectionExtension वर्ग शुरू किया गया है और डिफ़ॉल्ट मान प्राप्त करने के लिए GetValueOrDefault विधि का उपयोग कर सकते हैं यदि कुंजी किसी शब्दकोश में नहीं है।

Dictionary<string, string> colorData = new  Dictionary<string, string>();
string color = colorData.GetValueOrDefault("colorId", string.Empty);

4

Collections.Specialized.StringDictionaryअनुपलब्ध कुंजी का मान देखते हुए एक गैर-अपवाद परिणाम प्रदान करता है। यह डिफ़ॉल्ट रूप से केस-असंवेदनशील भी है।

चेतावनियां

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


4

यदि आप .Net Core का उपयोग कर रहे हैं, तो आप CollectionExtensions.GetValueOrDefault विधि का उपयोग कर सकते हैं । यह स्वीकार किए गए उत्तर में प्रदान किए गए कार्यान्वयन के समान है।

public static TValue GetValueOrDefault<TKey,TValue> (
   this System.Collections.Generic.IReadOnlyDictionary<TKey,TValue> dictionary,
   TKey key);

3
public class DefaultIndexerDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    private IDictionary<TKey, TValue> _dict = new Dictionary<TKey, TValue>();

    public TValue this[TKey key]
    {
        get
        {
            TValue val;
            if (!TryGetValue(key, out val))
                return default(TValue);
            return val;
        }

        set { _dict[key] = value; }
    }

    public ICollection<TKey> Keys => _dict.Keys;

    public ICollection<TValue> Values => _dict.Values;

    public int Count => _dict.Count;

    public bool IsReadOnly => _dict.IsReadOnly;

    public void Add(TKey key, TValue value)
    {
        _dict.Add(key, value);
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        _dict.Add(item);
    }

    public void Clear()
    {
        _dict.Clear();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _dict.Contains(item);
    }

    public bool ContainsKey(TKey key)
    {
        return _dict.ContainsKey(key);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        _dict.CopyTo(array, arrayIndex);
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _dict.GetEnumerator();
    }

    public bool Remove(TKey key)
    {
        return _dict.Remove(key);
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        return _dict.Remove(item);
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return _dict.TryGetValue(key, out value);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _dict.GetEnumerator();
    }
}

1

कोई शब्दकोश की कुंजी-लुकअप फ़ंक्शन के लिए एक इंटरफ़ेस परिभाषित कर सकता है। मैं शायद इसे कुछ इस तरह परिभाषित करूंगा:

Interface IKeyLookup(Of Out TValue)
  Function Contains(Key As Object)
  Function GetValueIfExists(Key As Object) As TValue
  Function GetValueIfExists(Key As Object, ByRef Succeeded As Boolean) As TValue
End Interface

Interface IKeyLookup(Of In TKey, Out TValue)
  Inherits IKeyLookup(Of Out TValue)
  Function Contains(Key As TKey)
  Function GetValue(Key As TKey) As TValue
  Function GetValueIfExists(Key As TKey) As TValue
  Function GetValueIfExists(Key As TKey, ByRef Succeeded As Boolean) As TValue
End Interface

गैर-जेनेरिक कुंजी वाला संस्करण कोड को अनुमति देता है जो कि गैर-संरचना कुंजी प्रकारों का उपयोग करके कोड का उपयोग कर मनमाना कुंजी विचरण के लिए अनुमति देता है, जो कि सामान्य प्रकार के पैरामीटर के साथ संभव नहीं होगा। एक को एक उत्परिवर्ती के Dictionary(Of Cat, String)रूप में एक उत्परिवर्ती का उपयोग करने की अनुमति नहीं दी जानी चाहिए Dictionary(Of Animal, String)क्योंकि उत्तरार्द्ध की अनुमति होगी SomeDictionaryOfCat.Add(FionaTheFish, "Fiona")। लेकिन Dictionary(Of Cat, String)एक अपरिवर्तनीय के रूप में एक उत्परिवर्ती का उपयोग करने के साथ कुछ भी गलत नहीं है Dictionary(Of Animal, String), क्योंकि SomeDictionaryOfCat.Contains(FionaTheFish)इसे पूरी तरह से अच्छी तरह से बनाई गई अभिव्यक्ति माना जाना चाहिए (इसे वापस आना चाहिए false, बिना शब्दकोश की खोज के, जो कुछ भी प्रकार का नहीं है Cat)।

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


1

यदि आप ASP.NET MVC का उपयोग कर रहे हैं, तो आप रूटवैल्यूडर क्लास का लाभ उठा सकते हैं जो काम करते हैं।

public object this[string key]
{
  get
  {
    object obj;
    this.TryGetValue(key, out obj);
    return obj;
  }
  set
  {
    this._dictionary[key] = value;
  }
}

1

यह सवाल इस बात की पुष्टि करने में मदद मिली है कि TryGetValueनाटकों FirstOrDefaultभूमिका यहाँ।

एक दिलचस्प सी # 7 सुविधा जिसका मैं उल्लेख करना चाहूंगा वह है आउट वेरिएबल्स फीचर, और यदि आप समीकरण के लिए C # 6 से नल-सशर्त ऑपरेटर जोड़ते हैं तो आपका कोड अतिरिक्त एक्सटेंशन विधियों की आवश्यकता के साथ और भी सरल हो सकता है।

var dic = new Dictionary<string, MyClass>();
dic.TryGetValue("Test", out var item);
item?.DoSomething();

इसका नकारात्मक पक्ष यह है कि आप इस तरह से सब कुछ इनलाइन नहीं कर सकते हैं;

dic.TryGetValue("Test", out var item)?.DoSomething();

अगर हमें इसकी आवश्यकता है / हम ऐसा करना चाहते हैं तो हमें जॉन की तरह एक विस्तार विधि को कोड करना चाहिए।


1

यहाँ C # 7.1 की दुनिया के लिए @ JonSkeet का एक संस्करण है जो वैकल्पिक डिफ़ॉल्ट को भी पास करने की अनुमति देता है:

public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue = default) => dict.TryGetValue(key, out TV value) ? value : defaultValue;

जिस मामले में आप लौटना चाहते हैं, उसे अनुकूलित करने के लिए दो कार्य करना अधिक कुशल हो सकता है default(TV):

public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
public static TV GetValueOrDefault2<TK, TV>(this IDictionary<TK, TV> dict, TK key) {
    dict.TryGetValue(key, out TV value);
    return value;
}

दुर्भाग्यवश C # नहीं है (अभी तक!) में अल्पविराम ऑपरेटर (या C # 6 प्रस्तावित अर्धविराम ऑपरेटर) है, इसलिए आपको ओवरलोड में से एक के लिए एक वास्तविक फ़ंक्शन बॉडी (हांफना) होना चाहिए।


1

मैं एक STL के नक्शे के समान व्यवहार के साथ एक IDEDIA बनाने के लिए एनकैप्सुलेशन का उपयोग करता था , आप में से उन लोगों के लिए जो c ++ से परिचित हैं। जो नहीं कर रहे हैं के लिए:

  • यदि कोई कुंजी मौजूद नहीं है, तो इंडेक्सर सुरक्षित स्थान पर {} नीचे डिफ़ॉल्ट मान लौटाता है, और डिफ़ॉल्ट मान के साथ शब्दकोश में उस कुंजी को जोड़ता है। यह अक्सर वांछित व्यवहार होता है, जैसा कि आप उन वस्तुओं को देख रहे हैं जो अंततः दिखाई देंगे या दिखाई देने का एक अच्छा मौका होगा।
  • विधि Add (TK key, TV val) एक AddOrUpdate विधि के रूप में व्यवहार करता है, जो मान को वर्तमान में रखता है यदि वह फेंकने के बजाय मौजूद है। मैं यह नहीं देखता कि $ $ में AddOrUpdate पद्धति क्यों नहीं है और सोचता है कि बहुत ही सामान्य परिदृश्यों में त्रुटियों को फेंकना एक अच्छा विचार है।

टीएल / डीआर - सुरक्षित नाम को किसी भी परिस्थिति में अपवादों को फेंकने के लिए नहीं लिखा जाता है, अन्य विकृत परिदृश्यों के अलावा , जैसे कि कंप्यूटर मेमोरी से बाहर (या आग पर)। यह AddOrUpdate व्यवहार के साथ Add की जगह और Indexer से NotFoundException को फेंकने के बजाय डिफ़ॉल्ट रूप से वापस आता है।

यहाँ कोड है:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

public class SafeDictionary<TK, TD>: IDictionary<TK, TD> {
    Dictionary<TK, TD> _underlying = new Dictionary<TK, TD>();
    public ICollection<TK> Keys => _underlying.Keys;
    public ICollection<TD> Values => _underlying.Values;
    public int Count => _underlying.Count;
    public bool IsReadOnly => false;

    public TD this[TK index] {
        get {
            TD data;
            if (_underlying.TryGetValue(index, out data)) {
                return data;
            }
            _underlying[index] = default(TD);
            return default(TD);
        }
        set {
            _underlying[index] = value;
        }
    }

    public void CopyTo(KeyValuePair<TK, TD>[] array, int arrayIndex) {
        Array.Copy(_underlying.ToArray(), 0, array, arrayIndex,
            Math.Min(array.Length - arrayIndex, _underlying.Count));
    }


    public void Add(TK key, TD value) {
        _underlying[key] = value;
    }

    public void Add(KeyValuePair<TK, TD> item) {
        _underlying[item.Key] = item.Value;
    }

    public void Clear() {
        _underlying.Clear();
    }

    public bool Contains(KeyValuePair<TK, TD> item) {
        return _underlying.Contains(item);
    }

    public bool ContainsKey(TK key) {
        return _underlying.ContainsKey(key);
    }

    public IEnumerator<KeyValuePair<TK, TD>> GetEnumerator() {
        return _underlying.GetEnumerator();
    }

    public bool Remove(TK key) {
        return _underlying.Remove(key);
    }

    public bool Remove(KeyValuePair<TK, TD> item) {
        return _underlying.Remove(item.Key);
    }

    public bool TryGetValue(TK key, out TD value) {
        return _underlying.TryGetValue(key, out value);
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return _underlying.GetEnumerator();
    }
}


0

इस एक-लाइनर के बारे में क्या है जो यह जांचता है कि क्या कुंजी का उपयोग कर मौजूद है ContainsKeyऔर फिर सशर्त ऑपरेटर का उपयोग करके या तो सामान्य रूप से पुनर्प्राप्त मूल्य या डिफ़ॉल्ट मान लौटाता है?

var myValue = myDictionary.ContainsKey(myKey) ? myDictionary[myKey] : myDefaultValue;

एक नया शब्दकोश वर्ग लागू करने की आवश्यकता नहीं है जो डिफ़ॉल्ट मानों का समर्थन करता है, बस अपने लुकअप स्टेटमेंट को ऊपर की छोटी लाइन के साथ बदलें।


5
यह एक के बजाय दो लुकअप है और यदि आप समवर्ती छाया का उपयोग कर रहे हैं तो यह एक दौड़ की स्थिति का परिचय देता है।
piedar

0

TryGetValueयदि यह है तो डिफ़ॉल्ट मान की जाँच करने और वापस करने के लिए यह एक-लाइनर हो सकता है false

 Dictionary<string, int> myDic = new Dictionary<string, int>() { { "One", 1 }, { "Four", 4} };
 string myKey = "One"
 int value = myDic.TryGetValue(myKey, out value) ? value : 100;

myKey = "One" => value = 1

myKey = "two" => value = 100

myKey = "Four" => value = 4

इसे ऑनलाइन आज़माएं


-1

नहीं, क्योंकि अन्यथा आप उस अंतर को कैसे जान पाएंगे जब कुंजी मौजूद है लेकिन एक अशक्त मान संग्रहीत है? यह महत्वपूर्ण हो सकता है।


11
यह हो सकता है - लेकिन कुछ स्थितियों में आप अच्छी तरह से जान सकते हैं कि यह नहीं है। मैं इसे कभी-कभी उपयोगी देख सकता हूं।
जॉन स्कीट

8
यदि आपको पता है कि अशक्त नहीं है - यह बहुत अच्छा है। यह Linq में इन चीजों में शामिल हो जाता है (जो कि मैं अभी हाल ही में करता हूं) इतना आसान है
TheSoftwareJedi

1
ContainsKey विधि का उपयोग करके?
मैटवेवी

4
हां, यह वास्तव में बहुत उपयोगी है। अजगर के पास यह है और मैं इसे सादे विधि से कहीं अधिक उपयोग करता हूं, जब मैं अजगर में होता हूं। यदि अतिरिक्त परिणाम के लिए जाँच की जा रही है तो बयान लिखने से बहुत अधिक बचत होती है। (आप सही कह रहे हैं, बेशक, यह अशक्त कभी-कभी उपयोगी होता है, लेकिन आमतौर पर मैं इसके बजाय एक "" या 0 चाह रहा हूं।)
जॉन कॉम्ब्स

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