सी # में टुपल्स (या सरणियाँ) शब्दकोश कुंजी के रूप में


109

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

जवाबों:


114

यदि आप .NET 4.0 पर हैं तो एक ट्यूपल का उपयोग करें:

lookup = new Dictionary<Tuple<TypeA, TypeB, TypeC>, string>();

यदि आप एक टपल को परिभाषित नहीं कर सकते हैं और कुंजी के रूप में उपयोग कर सकते हैं। Tuple को GetHashCode, Equals और IEquatable को ओवरराइड करने की आवश्यकता है:

struct Tuple<T, U, W> : IEquatable<Tuple<T,U,W>>
{
    readonly T first;
    readonly U second;
    readonly W third;

    public Tuple(T first, U second, W third)
    {
        this.first = first;
        this.second = second;
        this.third = third;
    }

    public T First { get { return first; } }
    public U Second { get { return second; } }
    public W Third { get { return third; } }

    public override int GetHashCode()
    {
        return first.GetHashCode() ^ second.GetHashCode() ^ third.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }
        return Equals((Tuple<T, U, W>)obj);
    }

    public bool Equals(Tuple<T, U, W> other)
    {
        return other.first.Equals(first) && other.second.Equals(second) && other.third.Equals(third);
    }
}

6
इस संरचना को IEquatable <Tuple <T, U, W >> भी लागू करना चाहिए। जब आप हैश कोड टकराव के मामले में इक्वाल्स () कहते हैं तो इस तरह से आप मुक्केबाजी से बच सकते हैं।
डस्टिन कैंपबेल

16
@jerryjvl और बाकी सभी लोग जो इसे Google द्वारा खोजते हैं जैसे मैंने किया था। .NET 4 का टपल इम्प्रूव बराबर होता है इसलिए इसे डिक्शनरी में इस्तेमाल किया जा सकता है।
स्कॉट चैंबरलेन

30
आपका GetHashCodeकार्यान्वयन बहुत अच्छा नहीं है। यह खेतों के क्रमपरिवर्तन के तहत अपरिवर्तनीय है।
कोडइंचो

2
ट्यूपल एक संरचना नहीं होना चाहिए। रूपरेखा में, टपल एक संदर्भ प्रकार है।
माइकल ग्रेस्कक

5
@ थोड़ोट - बेशक आपका उदाहरण गलत है ... यह होना चाहिए। new object()दूसरे के बराबर क्यों होगा new object()? यह केवल सीधे संदर्भ कॉमरिसन का उपयोग नहीं करता है ... कोशिश करें:bool test = new Tuple<int, string>(1, "foo").Equals(new Tuple<int, string>(1, "Foo".ToLower()));
माइक मैरीनोव्स्की

35

टपल और नेस्टेड शब्दकोशों आधारित दृष्टिकोणों के बीच, टपल आधारित के लिए जाना लगभग हमेशा बेहतर होता है।

स्थिरता के दृष्टिकोण से ,

  • इसकी कार्यक्षमता को लागू करने के लिए बहुत आसान है जो दिखता है:

    var myDict = new Dictionary<Tuple<TypeA, TypeB, TypeC>, string>();

    से

    var myDict = new Dictionary<TypeA, Dictionary<TypeB, Dictionary<TypeC, string>>>();

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

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

प्रदर्शन के दृष्टिकोण से , आप जिस सर्वोत्तम निष्कर्ष तक पहुँच सकते हैं, वह है इसे स्वयं मापना। लेकिन कुछ सैद्धांतिक सीमाएँ हैं जिन पर आप पहले से विचार कर सकते हैं:

  • नेस्टेड डिक्शनरी मामले में, हर कुंजी (बाहरी और भीतरी) के लिए एक अतिरिक्त डिक्शनरी होने से कुछ मेमोरी ओवरहेड होगी (टपल बनाने से अधिक)।

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

  • Tuple दृष्टिकोण के बारे में, .NET ट्यूपल्स सबसे अधिक प्रदर्शन करने वाले नहीं हैं, जब उनका उपयोग सेट के रूप में कुंजी के रूप में किया जाना है क्योंकि इसके कार्यान्वयन Equalsऔर GetHashCodeमूल्य प्रकारों के लिए मुक्केबाजी का कारण बनता है

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


साइड नोट पर, कुछ सौंदर्य प्रसाधन शब्दकोश को ठंडा कर सकते हैं:

  1. इंडेक्सर स्टाइल कॉल एक बहुत क्लीनर और सहज ज्ञान युक्त हो सकता है। उदाहरण के लिए,

    string foo = dict[a, b, c]; //lookup
    dict[a, b, c] = ""; //update/insertion

    तो अपने शब्दकोश वर्ग में आवश्यक इंडेक्सर्स को उजागर करें जो आंतरिक रूप से सम्मिलन और लुकअप को संभालता है।

  2. इसके अलावा, एक उपयुक्त IEnumerableइंटरफ़ेस लागू करें और एक Add(TypeA, TypeB, TypeC, string)विधि प्रदान करें जो आपको संग्रह आरम्भिक सिंटैक्स प्रदान करे, जैसे:

    new MultiKeyDictionary<TypeA, TypeB, TypeC, string> 
    { 
        { a, b, c, null }, 
        ...
    };

नेस्टेड शब्दकोशों के मामले में, इंडेक्स सिंटैक्स इस तरह अधिक नहीं होगा string foo = dict[a][b][c]:?
स्टीवन रैंड

@StevenRands हाँ यह होगा।
नवाफाल

1
@nawfal क्या मैं तुषार शब्दकोश खोज सकता हूं जब मेरे पास केवल एक ही कुंजी है? या क्या मैं इस तरह का [a, b] फिर तानाशाह [a, c] कर सकता हूँ?
खान इंजीनियर

@KhanEngineer इसका बहुत कुछ इस बात पर निर्भर करता है कि शब्दकोश का उद्देश्य क्या है या आप इसका उपयोग कैसे करना चाहते हैं। उदाहरण के लिए, आप, कुंजी का एक हिस्सा द्वारा मूल्य वापस प्राप्त करना चाहते हैं a। आप किसी भी सामान्य संग्रह की तरह ही किसी भी शब्दकोश को पुनरावृत्त कर सकते हैं और यदि यह है तो महत्वपूर्ण संपत्ति की जांच करें a। यदि आप हमेशा पहली संपत्ति के हिसाब से आइटम को डिक्टेट करना चाहते हैं तो आप शब्दकोश के शब्दकोश को बेहतर तरीके से डिजाइन कर सकते हैं जैसा कि मेरे उत्तर और क्वेरी में दिखाया गया है dict[a], जो आपको एक और शब्दकोश देता है।
नवफाल

यदि "केवल एक कुंजी द्वारा खोजें" का अर्थ है कि आपके पास मौजूद किसी भी कुंजी द्वारा मूल्य वापस प्राप्त करना है, तो आप अपने शब्दकोश को "किसी भी महत्वपूर्ण शब्दकोश" के एक प्रकार के रूप में बेहतर बनाते हैं। उदाहरण के लिए आप लाभ प्राप्त करना चाहते है, तो 4दोनों चाबी के लिए aऔर bफिर तुम यह एक मानक शब्दकोश बनाने के लिए और की तरह मान जोड़ सकते हैं, dict[a] = 4और dict[b] = 4। यह समझ में नहीं आ सकता है कि क्या तार्किक रूप से आपका aऔर bएक इकाई होना चाहिए। ऐसे मामले में आप एक रिवाज को परिभाषित कर सकते हैं IEqualityComparerजो दो प्रमुख स्थानों को समान बनाता है यदि उनके किसी भी गुण के बराबर हैं। यह सब उदारता के साथ किया जा सकता है।
नवाफाल

30

यदि आप C # 7 पर हैं, तो आपको अपनी समग्र कुंजी के रूप में मूल्य tuples का उपयोग करने पर विचार करना चाहिए। वैल्यू टुपल्स आमतौर पर पारंपरिक रेफरेंस ट्यूपल्स ( Tuple<T1, …>) से बेहतर परफॉर्मेंस देते हैं क्योंकि वैल्यू ट्यूपल्स वैल्यू टाइप (स्ट्रक्चर्स) होते हैं, रेफरेंस टाइप्स नहीं, इसलिए वे मेमोरी एलोकेशन और गारबेज कलेक्शन कॉस्ट से बच जाते हैं। इसके अलावा, वे अपने क्षेत्रों के नाम रखने के लिए अनुमति देते हैं, तो आप उन्हें और अधिक सहज ज्ञान युक्त वाक्यविन्यास प्रदान करते हैं। वे IEquatable<T>शब्दकोश के लिए आवश्यक इंटरफ़ेस भी लागू करते हैं ।

var dict = new Dictionary<(int PersonId, int LocationId, int SubjectId), string>();
dict.Add((3, 6, 9), "ABC");
dict.Add((PersonId: 4, LocationId: 9, SubjectId: 10), "XYZ");
var personIds = dict.Keys.Select(k => k.PersonId).Distinct().ToList();

13

अच्छा, साफ, तेज, आसान और पठनीय तरीका है:

  • वर्तमान प्रकार के लिए समानता सदस्य (बराबर) और GetHashCode () विधि उत्पन्न करें। ReSharper जैसे उपकरण न केवल विधियों का निर्माण करते हैं, बल्कि एक समानता की जांच के लिए आवश्यक कोड और / या हैश कोड की गणना के लिए भी उत्पन्न करते हैं। जनरेट कोड टपल अहसास की तुलना में अधिक इष्टतम होगा।
  • बस एक साधारण कुंजी वर्ग को टुप से बनाया गया है

कुछ इसी तरह जोड़ें:

public sealed class myKey : Tuple<TypeA, TypeB, TypeC>
{
    public myKey(TypeA dataA, TypeB dataB, TypeC dataC) : base (dataA, dataB, dataC) { }

    public TypeA DataA => Item1; 

    public TypeB DataB => Item2;

    public TypeC DataC => Item3;
}

तो आप इसे शब्दकोश के साथ उपयोग कर सकते हैं:

var myDictinaryData = new Dictionary<myKey, string>()
{
    {new myKey(1, 2, 3), "data123"},
    {new myKey(4, 5, 6), "data456"},
    {new myKey(7, 8, 9), "data789"}
};
  • आप इसे अनुबंधों में भी उपयोग कर सकते हैं
  • linq में join या groupings के लिए एक कुंजी के रूप में
  • इस तरह से आप कभी भी Item1, Item2, Item3 के गलत ऑर्डर को कभी नहीं ...
  • आपको कुछ पाने के लिए समझने के लिए कोड को याद करने या देखने की आवश्यकता नहीं है
  • IStructuralEquatable, IStructuralComparable, IComparable, ITuple को ओवरराइड करने की कोई आवश्यकता नहीं है

1
अब आप अभिव्यक्ति का उपयोग करने वाले सदस्यों को इसके क्लीनर की तरह इस्तेमाल कर सकते हैंpublic TypeA DataA => Item1;
andrewtatham

7

यदि किसी कारण से आप वास्तव में अपनी खुद की ट्यूपल क्लास बनाने से बचना चाहते हैं, या बिल्ट-इन .NET 4.0 का उपयोग कर रहे हैं, तो एक अन्य दृष्टिकोण संभव है; आप एक ही मूल्य में तीन प्रमुख मूल्यों को एक साथ जोड़ सकते हैं।

उदाहरण के लिए, यदि तीन मान पूर्णांक प्रकार एक साथ 64 बिट से अधिक नहीं ले रहे हैं, तो आप उन्हें एक में जोड़ सकते हैं ulong

सबसे खराब स्थिति आप हमेशा एक स्ट्रिंग का उपयोग कर सकते हैं, जब तक आप यह सुनिश्चित कर लेते हैं कि इसमें तीन घटक कुछ वर्ण या अनुक्रम के साथ सीमांकित हैं जो कुंजी के घटकों के अंदर नहीं होते हैं, उदाहरण के लिए, तीन संख्याओं के साथ आप कोशिश कर सकते हैं:

string.Format("{0}#{1}#{2}", key1, key2, key3)

इस दृष्टिकोण में स्पष्ट रूप से कुछ रचना ओवरहेड है, लेकिन आप इसके लिए इसका क्या उपयोग कर रहे हैं, इसके आधार पर यह तुच्छ हो सकता है कि इसकी परवाह न करें।


6
मैं कहूंगा कि यह हालांकि संदर्भ पर दृढ़ता से निर्भर करता है; अगर मेरे पास संयोजन करने के लिए तीन पूर्णांक प्रकार हैं, और प्रदर्शन महत्वपूर्ण नहीं था, तो यह गलती करने के न्यूनतम अवसर के साथ पूरी तरह से ठीक है। बेशक, यह सब .NET 4 के रूप में पूरी तरह से बेमानी है, क्योंकि Microsoft हमें प्रदान करेगा (संभवतः सही!) बॉक्स के बाहर टपल टाइप करें।
jerryjvl

यहां तक ​​कि आप इस पद्धति का उपयोग कर सकते हैं संयोजन के साथ एक के JavaScriptSerializerलिए स्ट्रिंग और / या पूर्णांक प्रकारों की एक सरणी संक्षिप्त करने के लिए। इस तरह, आपको स्वयं एक सीमांकित चरित्र के साथ आने की आवश्यकता नहीं है।
बिंकी जू

3
इस कुंजी के किसी भी करता है, तो असली गंदा हो सकता है ( key1, key2,key3 ) deliminator (युक्त तार थे "#")
ग्रेग

4

मैं एक उचित GetHashCode के साथ आपके Tuple को ओवरराइड करूंगा, और इसे कुंजी के रूप में उपयोग करूंगा।

जब तक आप उचित तरीकों को अधिभारित करते हैं, आपको सभ्य प्रदर्शन देखना चाहिए।


1
IComparable का इस बात पर कोई प्रभाव नहीं पड़ता है कि किस तरह से कुंजीशब्दों को किसी शब्दकोश <TKey, TValue> में संग्रहीत या स्थित किया जाता है। यह सब GetHashCode () और एक IEqualityComparer <T> के माध्यम से किया जाता है। IEquitable <T> को लागू करने से बेहतर प्रदर्शन प्राप्त होगा क्योंकि यह डिफ़ॉल्ट EqualityComparer की वजह से बॉक्सिंग को कम करता है, जो बराबर (ऑब्जेक्ट) फ़ंक्शन पर वापस आता है।
डस्टिन कैंपबेल

मैं GetHashCode का उल्लेख करने जा रहा था, लेकिन मुझे लगा कि डिक्शनरी ने IComparable का इस्तेमाल इस मामले में किया कि हैशकोड्स आइडेंटिकल थे ... लगता है कि मैं गलत था।
जॉन गित्जन

3

यहाँ संदर्भ के लिए .NET tuple है:

[Serializable] 
public class Tuple<T1, T2, T3> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple {

    private readonly T1 m_Item1; 
    private readonly T2 m_Item2;
    private readonly T3 m_Item3; 

    public T1 Item1 { get { return m_Item1; } }
    public T2 Item2 { get { return m_Item2; } }
    public T3 Item3 { get { return m_Item3; } } 

    public Tuple(T1 item1, T2 item2, T3 item3) { 
        m_Item1 = item1; 
        m_Item2 = item2;
        m_Item3 = item3; 
    }

    public override Boolean Equals(Object obj) {
        return ((IStructuralEquatable) this).Equals(obj, EqualityComparer<Object>.Default);; 
    }

    Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) { 
        if (other == null) return false;

        Tuple<T1, T2, T3> objTuple = other as Tuple<T1, T2, T3>;

        if (objTuple == null) {
            return false; 
        }

        return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3); 
    }

    Int32 IComparable.CompareTo(Object obj) {
        return ((IStructuralComparable) this).CompareTo(obj, Comparer<Object>.Default);
    }

    Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) {
        if (other == null) return 1; 

        Tuple<T1, T2, T3> objTuple = other as Tuple<T1, T2, T3>;

        if (objTuple == null) {
            throw new ArgumentException(Environment.GetResourceString("ArgumentException_TupleIncorrectType", this.GetType().ToString()), "other");
        }

        int c = 0;

        c = comparer.Compare(m_Item1, objTuple.m_Item1); 

        if (c != 0) return c; 

        c = comparer.Compare(m_Item2, objTuple.m_Item2);

        if (c != 0) return c; 

        return comparer.Compare(m_Item3, objTuple.m_Item3); 
    } 

    public override int GetHashCode() { 
        return ((IStructuralEquatable) this).GetHashCode(EqualityComparer<Object>.Default);
    }

    Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) { 
        return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3));
    } 

    Int32 ITuple.GetHashCode(IEqualityComparer comparer) {
        return ((IStructuralEquatable) this).GetHashCode(comparer); 
    }
    public override string ToString() {
        StringBuilder sb = new StringBuilder();
        sb.Append("("); 
        return ((ITuple)this).ToString(sb);
    } 

    string ITuple.ToString(StringBuilder sb) {
        sb.Append(m_Item1); 
        sb.Append(", ");
        sb.Append(m_Item2);
        sb.Append(", ");
        sb.Append(m_Item3); 
        sb.Append(")");
        return sb.ToString(); 
    } 

    int ITuple.Size { 
        get {
            return 3;
        }
    } 
}

3
गेट हैश कोड के रूप में लागू किया जाता है ((
आइटम

2

यदि आपका उपभोग कोड, आईडी के बजाय एक IDEDIA <> इंटरफ़ेस के साथ कर सकता है, तो मेरी वृत्ति एक कस्टम सरणी तुलनित्र के साथ, क्रमबद्धता <> का उपयोग करना होगा:

class ArrayComparer<T> : IComparer<IList<T>>
    where T : IComparable<T>
{
    public int Compare(IList<T> x, IList<T> y)
    {
        int compare = 0;
        for (int n = 0; n < x.Count && n < y.Count; ++n)
        {
            compare = x[n].CompareTo(y[n]);
        }
        return compare;
    }
}

और इस प्रकार (int [] सिर्फ ठोस उदाहरण के लिए) का निर्माण करें:

var dictionary = new SortedDictionary<int[], string>(new ArrayComparer<int>());
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.