2010-09-13 21 views
5

सी # में LINQ का उपयोग कर

Dictioanry<String,List<String>> into Dictionary<String,String> 

मैं की तरह

Dictioanry<String,List<String>>dictOne=new Dictionary<String,List<String>>(); 

एक शब्दकोश हो रही है और जो

Key(String)   Value(List<String>) 

    A    a1,a2 
    B    b1,b2 
    C    c1 

containg मैं "dictOne बदलने की आवश्यकता कन्वर्ट करने के लिए कैसे शब्दकोश पीछे "

Dictionary<String,String> dictReverse=new Dictionary<String,String>() 
में

तो परिणाम होगा की तरह

Key(String)   Value(String) 

    a1     A 
    a2     A 
    b1     B 
    b2     B 
    c1     C 

वहाँ अग्रिम

+0

क्या LINQ में ऐसा कोई विशिष्ट कारण है? –

+0

@ माइकल शिममिन्स: अब मैं फ़ोरैच लूप का उपयोग कर रहा हूं। excecution प्रतिस्पर्धा करने के लिए और अधिक समय ले रहा है (मुझे 2000 से अधिक कुंजी और प्रत्येक मान (सूची) में कम से कम 5 आइटम हैं) –

+0

मुझे संदेह है कि LINQ कोई तेज होगा, शायद धीमा हो जाएगा। –

उत्तर

12

अद्यतन: दूसरों के रूप में उल्लेख किया है, ताकि एक शब्दकोश इस तरह से सही मायने में "प्रतिवर्ती" होने के लिए में, अपने List<string> वस्तुओं में मूल्यों सभी के लिए अद्वितीय हो की जरूरत है; अन्यथा, आप अपने स्रोत शब्दकोश में प्रत्येक मान के लिए एक प्रविष्टि के साथ Dictionary<string, string> नहीं बना सकते हैं, क्योंकि डुप्लिकेट कुंजी होगी।

उदाहरण:

var dictOne = new Dictionary<string, List<string>> 
{ 
    { "A", new List<string> { "a1", "a2" } }, 
    { "B", new List<string> { "b1", "b2" } }, 
    { "C", new List<string> { "c1", "a2" } } // duplicate! 
}; 

आपके पास (कम से कम) इस से निपटने के लिए दो विकल्प।

विकल्प 1: वास्तव में, डुप्लिकेट

आप यह सुनिश्चित करें कि हर List<string> में हर तत्व है चाहते हो सकता है पर फेंक, अद्वितीय। इस मामले में, के साथ एक साधारण SelectMany आपको जो चाहिए वह पूरा करेगा; ToDictionary कॉल एक डुप्लिकेट मान का सामना पर एक ArgumentException फेंक देगा: अपनी ही विधि में इस कार्यशीलता सार को

var dictTwo = dictOne 
    .SelectMany(kvp => kvp.Value.Select(s => new { Key = s, Value = kvp.Key })) 
    .ToDictionary(x => x.Key, x => x.Value); 

सबसे सामान्य तरीके से (जो मन में आता) एक विस्तार विधि है कि किसी के लिए ऐसा करता है लागू करने के लिए किया जाएगा IDictionary<T, TEnumerable> कार्यान्वयन जहां TEnumerable लागू करता IEnumerable<TValue>:

// Code uglified to fit within horizonal scroll area 
public static Dictionary<T2, T1> ReverseDictionary<T1, T2, TEnumerable>(
    this IDictionary<T1, TEnumerable> source) where TEnumerable : IEnumerable<T2> 
{ 
    return source 
     .SelectMany(e => e.Value.Select(s => new { Key = s, Value = e.Key })) 
     .ToDictionary(x => x.Key, x => x.Value); 
} 

उपरोक्त विधि में सामान्य प्रकार पैरामीटर की बदसूरत प्रसार सख्ती से Dictionary<T, List<T>> के अलावा अन्य प्रकार के लिए अनुमति देने के लिए है: यह एकस्वीकार कर सकता है, उदाहरण के लिए, या SortedList<string, Queue<DateTime>> - इसकी लचीलापन का प्रदर्शन करने के लिए केवल कुछ मनमाना उदाहरण हैं।

(एक परीक्षण कार्यक्रम इस विधि को दर्शाता हुआ इस उत्तर के नीचे स्थित है।) विकल्प 2: डुप्लिकेट छोड़ें

अपने List<string> मूल्यों में डुप्लिकेट तत्वों को एक यथार्थवादी परिदृश्य है कि आप में सक्षम होना चाहते है अपवाद फेंकने के बिना संभालने के लिए, मेरा सुझाव है कि का उपयोग करने वाले दृष्टिकोण के लिए Gabe's excellent answer पर एक नज़र डालें (वास्तव में, गेबे भी एक लचीला दृष्टिकोण प्रदान करता है जो चयनकर्ता फ़ंक्शन के आधार पर इन दोनों मामलों में से किसी एक को कवर कर सकता है; हालांकि, यदि आप निश्चित रूप से डुप्लिकेट पर फेंकना चाहते हैं, मैं अभी भी abov का सुझाव देना चाहता हूँ ई दृष्टिकोण, क्योंकि यह GroupBy का उपयोग करने से कुछ हद तक सस्ता होना चाहिए)।

उदाहरण कार्यक्रम

यहाँ एक छोटे से परीक्षण कार्यक्रम ऊपर को दर्शाता हुआ है एक साथ Dictionary<string, List<string>> कोई डुप्लिकेट तत्वों को अपने List<string> मूल्यों में पर विकल्प 1:

var dictOne = new Dictionary<string, List<string>> 
{ 
    { "A", new List<string> { "a1", "a2" } }, 
    { "B", new List<string> { "b1", "b2" } }, 
    { "C", new List<string> { "c1" } } 
}; 

// Using ReverseDictionary implementation described above: 
var dictTwo = dictOne.ReverseDictionary<string, string, List<string>>(); 

foreach (var entry in dictTwo) 
{ 
    Console.WriteLine("{0}: {1}", entry.Key, entry.Value); 
} 

आउटपुट:

 
a1: A 
a2: A 
b1: B 
b2: B 
c1: C 
+0

+1 - बस एक ही कोड लिखना समाप्त हो गया! – Will

+0

@Will: अच्छा, महान दिमाग एक जैसे सोचते हैं;) –

+0

+1 मैं दो 'से' के बजाय 'SelectMany' का उपयोग करके यह एक लाइन कोड भी पसंद करता हूं। –

9
// Associates each key with each of its values. Produces a sequence like: 
// {A, a1}, {A, a2}, {B, b1}, {B, b2}, {C, c1}    
var kvps = from kvp in dictOne 
      from value in kvp.Value 
      select new { Key = kvp.Key, Value = value };  

// Turns the sequence into a dictionary, with the old 'Value' as the new 'Key' 
var dictReverse = kvps.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); 
बेशक

में

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

यह भी ध्यान रखें कि Dictionary<K, V> किसी भी तरह के गणना आदेश को परिभाषित नहीं करता है। उचित क्रम में परिणामस्वरूप शब्दकोश को गणना करने के लिए आप Enumerable.OrderBy विधि का उपयोग कर सकते हैं।

+0

+1, और वास्तव में LINQ क्वेरी सिंटैक्स का उपयोग करने के लिए, मेरे विपरीत (मुझे नहीं पता कि मैं इसे इस तरह क्यों नहीं लिखता - यह स्पष्ट रूप से अधिक पठनीय है)। –

+0

आपको सॉर्ट करने की आवश्यकता क्यों होगी? आप एक unordered सेट से दूसरे unordered सेट में जा रहे हैं, तो सॉर्टिंग व्यर्थ होगा। – Gabe

+0

@ दैन ताओ: धन्यवाद। मैं विस्तार विधि वाक्यविन्यास के साथ और अधिक आरामदायक हूँ; मैं खुद को इस्तेमाल करने के लिए क्वेरी सिंटैक्स लिखना शुरू करने के लिए मजबूर कर रहा हूं। – Ani

6

उस घटना में जो आप करेंगे अपने परिणाम शब्दकोश में डुप्लिकेट कुंजी के साथ समाप्त करें, आपको उन चाबियों में से एक को चुनना होगा।

var dictReverse = (from kvp in dictOne 
        from value in kvp.Value 
        group kvp.Key by value) 
        .ToDictionary(grp => grp.Key, grp => grp.First()); 

इस इनपुट शब्दकोश को देखते हुए:

var dictOne = new Dictionary<string, IEnumerable<string>> { 
    { "C", new List<string> { "c1", "a2" } }, 
    { "B", new List<string> { "b1", "b2" } }, 
    { "A", new List<string> { "a1", "a2" } } }; 

परिणाम होगा:

c1: C 
a2: C 
b1: B 
b2: B 
a1: A

दान अंक के रूप में यहाँ एक कार्यान्वयन कि सिर्फ पहले एक देखता उठाता (First का उपयोग) है बाहर, आप डुप्लिकेट कुंजी के मामले में अलग व्यवहार चाहते हैं।

public static Dictionary<V, K> Transpose<K, V>(
    this Dictionary<K, IEnumerable<V>> dictOne, 
    Func<IEnumerable<K>, K> selector) 
{ 
    return (from kvp in dictOne 
      from V value in kvp.Value 
      group kvp.Key by value) 
       .ToDictionary(grp => grp.Key, grp => selector(grp)); 
} 

तो फिर तुम यह dictOne.Transpose(Enumerable.First) की तरह, एक अपवाद प्राप्त करने के लिए जब वहाँ एक नकली चाबी (अन्य पदों के व्यवहार) है कह सकते हैं इसके बाद के संस्करण व्यवहार पाने के लिए dictOne.Transpose(Enumerable.Single), dictOne.Transpose(Enumerable.Min) पहले एक लेने के लिए: आप इस समारोह बना सकते हैं शब्दावली से, या अपने स्वयं के कार्य में पास जो कुछ भी आपको चाहिए।

+0

+1: एक अच्छा विचार, मानते हुए कि ओपी डुप्लिकेट कुंजी छोड़ने में सक्षम होना चाहता है। यदि वह प्रत्येक 'सूची ' (प्रश्न से अस्पष्ट) में वस्तुओं की विशिष्टता सुनिश्चित करना चाहता है, तो वह डुप्लिकेट की स्थिति में अपवाद फेंकने के लिए * * ToDictionary' चाहता है। –

+0

दान: अच्छा बिंदु। मेरा संपादन देखें। – Gabe

+0

मुझे यह विचार पसंद है, हालांकि दुर्भाग्य से मुझे लगता है कि 'डिक्शनरी <के, आईन्यूमेरेबल >' एक तर्क के रूप में आप जितना चाहें उतना लचीला है: एक 'डिक्शनरी >' (ओपी के प्रश्न में) उदाहरण के लिए, काम नहीं करते हैं। निस्संदेह सरल समाधान एक 'IDictionary ' लेना है, जहां TENumerable: IEnumerable 'बाधा (मेरा अपडेट देखें)। –

संबंधित मुद्दे