Linq का उपयोग करके डिक्शनरी में कनवर्ट करें और डुप्लिकेट के बारे में चिंता न करें


163

मेरे पास व्यक्तिगत वस्तुओं की एक सूची है। मैं एक ऐसे डिक्शनरी में बदलना चाहता हूँ जहाँ कुंजी पहला और अंतिम नाम (संक्षिप्त) हो और मान व्यक्ति वस्तु हो।

मुद्दा यह है कि मेरे पास कुछ डुप्लिकेट किए गए लोग हैं, इसलिए यदि मैं इस कोड का उपयोग करता हूं, तो यह गलत है:

private Dictionary<string, Person> _people = new Dictionary<string, Person>();

_people = personList.ToDictionary(
    e => e.FirstandLastName,
    StringComparer.OrdinalIgnoreCase);

मुझे पता है कि यह अजीब लगता है, लेकिन मैं अब तक डुप्लिकेट नामों के बारे में परवाह नहीं करता हूं। अगर कई नाम हैं तो मैं सिर्फ एक को पकड़ना चाहता हूं। क्या वैसे भी मैं इस कोड को ऊपर लिख सकता हूं इसलिए यह केवल एक नाम लेता है और डुप्लिकेट पर नहीं उड़ाता है?


1
डुप्लिकेट (कुंजी के आधार पर), मुझे यकीन नहीं है कि क्या आप उन्हें रखना चाहते हैं या उन्हें खोना चाहते हैं? उन्हें रखने से एक Dictionary<string, List<Person>>(या समतुल्य) की आवश्यकता होगी ।
एंथनी Pegram

@ एंथनी पेग्राम - बस उनमें से एक को रखना चाहते हैं। मैंने प्रश्न को और अधिक स्पष्ट होने के लिए अद्यतन किया
leora

अच्छी तरह से आप टौडर करने से पहले अलग उपयोग कर सकते हैं। लेकिन आपको व्यक्ति वर्ग के लिए
इक्वाल्स

@ सुजीत। बाधा - आप पास करने के लिए एक समानता तुलनित्र भी बना सकते हैं Distinct
काइल डेलानी

जवाबों:


71

यहाँ स्पष्ट, गैर linq समाधान है:

foreach(var person in personList)
{
  if(!myDictionary.Keys.Contains(person.FirstAndLastName))
    myDictionary.Add(person.FirstAndLastName, person);
}

208
2007 को इतना ही :)
leora

3
मामले को अनदेखा नहीं है कि
onof

हाँ, समय के साथ हम .net 2.0 ढांचे से काम पर अद्यतन करते हैं ... @onof मामले को अनदेखा करना बिल्कुल कठिन नहीं है। बस अपरकेस में सभी कुंजियाँ जोड़ें।
कार्ला जूल 23'10

मैं इस मामले को असंवेदनशील कैसे
बनाऊंगा

11
या StringComparer के साथ डिक्शनरी बनाएं जो मामले की अनदेखी करेगा, यदि आपको जो चाहिए, तो आपका ऐड / चेकिंग कोड परवाह नहीं करता है कि आप मामले को अनदेखा कर रहे हैं या नहीं।
बाइनरी वॉरियर

423

LINQ समाधान:

// Use the first value in group
var _people = personList
    .GroupBy(p => p.FirstandLastName, StringComparer.OrdinalIgnoreCase)
    .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);

// Use the last value in group
var _people = personList
    .GroupBy(p => p.FirstandLastName, StringComparer.OrdinalIgnoreCase)
    .ToDictionary(g => g.Key, g => g.Last(), StringComparer.OrdinalIgnoreCase);

यदि आप एक गैर LINQ समाधान पसंद करते हैं तो आप कुछ इस तरह से कर सकते हैं:

// Use the first value in list
var _people = new Dictionary<string, Person>(StringComparer.OrdinalIgnoreCase);
foreach (var p in personList)
{
    if (!_people.ContainsKey(p.FirstandLastName))
        _people[p.FirstandLastName] = p;
}

// Use the last value in list
var _people = new Dictionary<string, Person>(StringComparer.OrdinalIgnoreCase);
foreach (var p in personList)
{
    _people[p.FirstandLastName] = p;
}

6
@ ल्यूक माइनर नोट: आपके दो स्निपेट समतुल्य नहीं हैं: LINQ वेरिएंट पहले तत्व को बरकरार रखता है, गैर-LINQ स्निपेट अंतिम तत्व को बरकरार रखता है?
तोंग

4
@toong: यह सच है और निश्चित रूप से ध्यान देने योग्य है। (हालांकि इस मामले में ओपी को इस बात की कोई परवाह नहीं है कि वे किस तत्व के साथ अंत करते हैं।)
ल्यूक

1
"पहले मूल्य" मामले के लिए: nonLinq समाधान दो बार शब्दकोश खोज करता है, लेकिन Linq निरर्थक वस्तु त्वरित और पुनरावृत्ति करता है। दोनों आदर्श नहीं हैं।
17

@SerG शुक्र है कि लुकअप डिक्शनरी को आमतौर पर O (1) ऑपरेशन माना जाता है और इसका नगण्य प्रभाव पड़ता है।
म्होलिस

43

डिस्टिंक्ट () और और ग्रुपिंग का उपयोग करते हुए एक लिनक-समाधान है:

var _people = personList
    .Select(item => new { Key = item.Key, FirstAndLastName = item.FirstAndLastName })
    .Distinct()
    .ToDictionary(item => item.Key, item => item.FirstFirstAndLastName, StringComparer.OrdinalIgnoreCase);

मुझे नहीं पता कि यह ल्यूक के समाधान की तुलना में अच्छा है लेकिन यह भी काम करता है।


क्या आप सुनिश्चित हैं कि काम करता है? आपके द्वारा बनाए गए नए संदर्भ प्रकार की तुलना करने के लिए डिस्टि्रक्ट कैसा है? मुझे लगता है कि आपको यह काम पाने के लिए किसी प्रकार के IEqualityComparer को डिस्टिंचेंट में पास करना होगा।
बजे साइमन गिल्बी जूल

5
मेरी पिछली टिप्पणी को नजरअंदाज करें। देखें stackoverflow.com/questions/543482/...
साइमन Gillbee

आप ओवरराइड करने के लिए चाहते हैं, तो कैसे अलग बाहर निर्धारित की जांच है stackoverflow.com/questions/489258/...
जेम्स मैकमोहन

30

यह लंबोदर अभिव्यक्ति के साथ काम करना चाहिए:

personList.Distinct().ToDictionary(i => i.FirstandLastName, i => i);

2
यह होना चाहिए:personList.Distinct().ToDictionary(i => i.FirstandLastName, i => i);
Gh61

4
यह तभी काम करेगा जब पर्सन वर्ग के लिए डिफॉल्ट IEqualityComparer पहले और अंतिम नाम की तुलना में मामले की अनदेखी कर रहा हो। अन्यथा ऐसे IEqualityComparer लिखें और प्रासंगिक डिस्टि्रक्ट अधिभार का उपयोग करें। ओपी की आवश्यकता से मेल खाने के लिए आपकी टॉडनक्वेरी विधि भी केस-असंवेदनशील होनी चाहिए।
जो

13

आप ToLookupLINQ फ़ंक्शन का भी उपयोग कर सकते हैं , जिसे आप एक शब्दकोश के साथ लगभग एकांतर स्थान पर उपयोग कर सकते हैं।

_people = personList
    .ToLookup(e => e.FirstandLastName, StringComparer.OrdinalIgnoreCase);
_people.ToDictionary(kl => kl.Key, kl => kl.First()); // Potentially unnecessary

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


8

आप टोब्रैड () के समान एक विस्तार विधि बना सकते हैं जिसमें अंतर यह है कि यह डुप्लिकेट की अनुमति देता है। कुछ इस तरह:

    public static Dictionary<TKey, TElement> SafeToDictionary<TSource, TKey, TElement>(
        this IEnumerable<TSource> source, 
        Func<TSource, TKey> keySelector, 
        Func<TSource, TElement> elementSelector, 
        IEqualityComparer<TKey> comparer = null)
    {
        var dictionary = new Dictionary<TKey, TElement>(comparer);

        if (source == null)
        {
            return dictionary;
        }

        foreach (TSource element in source)
        {
            dictionary[keySelector(element)] = elementSelector(element);
        }

        return dictionary; 
    }

इस मामले में, यदि डुप्लिकेट हैं, तो अंतिम मूल्य जीतता है।


7

डुप्लिकेट को समाप्त करने से निपटने के लिए, इसे लागू करें जो विधि IEqualityComparer<Person>में उपयोग किया जा सकता है Distinct(), और फिर आपका शब्दकोश प्राप्त करना आसान होगा। दिया हुआ:

class PersonComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.FirstAndLastName.Equals(y.FirstAndLastName, StringComparison.OrdinalIgnoreCase);
    }

    public int GetHashCode(Person obj)
    {
        return obj.FirstAndLastName.ToUpper().GetHashCode();
    }
}

class Person
{
    public string FirstAndLastName { get; set; }
}

अपना शब्दकोश प्राप्त करें:

List<Person> people = new List<Person>()
{
    new Person() { FirstAndLastName = "Bob Sanders" },
    new Person() { FirstAndLastName = "Bob Sanders" },
    new Person() { FirstAndLastName = "Jane Thomas" }
};

Dictionary<string, Person> dictionary =
    people.Distinct(new PersonComparer()).ToDictionary(p => p.FirstAndLastName, p => p);

2
        DataTable DT = new DataTable();
        DT.Columns.Add("first", typeof(string));
        DT.Columns.Add("second", typeof(string));

        DT.Rows.Add("ss", "test1");
        DT.Rows.Add("sss", "test2");
        DT.Rows.Add("sys", "test3");
        DT.Rows.Add("ss", "test4");
        DT.Rows.Add("ss", "test5");
        DT.Rows.Add("sts", "test6");

        var dr = DT.AsEnumerable().GroupBy(S => S.Field<string>("first")).Select(S => S.First()).
            Select(S => new KeyValuePair<string, string>(S.Field<string>("first"), S.Field<string>("second"))).
           ToDictionary(S => S.Key, T => T.Value);

        foreach (var item in dr)
        {
            Console.WriteLine(item.Key + "-" + item.Value);
        }

मैं आपको मिनिमल, कम्प्लीट और वेरिफ़ाइबल उदाहरण पढ़कर अपना उदाहरण सुधारने का सुझाव देता हूँ ।
इलगाला

2

यदि हम चाहते हैं कि रिटर्न में सभी व्यक्ति (केवल एक व्यक्ति के बजाय), तो हम यह कर सकते हैं:

var _people = personList
.GroupBy(p => p.FirstandLastName)
.ToDictionary(g => g.Key, g => g.Select(x=>x));

1
क्षमा करें, मेरे समीक्षा-संपादन को अनदेखा करें (मुझे नहीं पता कि मैं अपना समीक्षा-संपादन कहाँ हटाऊं)। मैं सिर्फ g.Select (x => x) के बजाय g.First () का उपयोग करने के बारे में एक सुझाव जोड़ना चाहता था।
एलेक्स 75

1

अधिकांश अन्य उत्तरों के साथ मुद्दा यह है कि वे उपयोग करते हैं Distinct, GroupByया ToLookup, जो हुड के तहत एक अतिरिक्त शब्दकोश बनाता है। समान रूप से ToUpper अतिरिक्त स्ट्रिंग बनाता है। मैंने ऐसा किया है, जो एक परिवर्तन को छोड़कर Microsoft के कोड की लगभग एक सटीक प्रतिलिपि है:

    public static Dictionary<TKey, TSource> ToDictionaryIgnoreDup<TSource, TKey>
        (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer = null) =>
        source.ToDictionaryIgnoreDup(keySelector, i => i, comparer);

    public static Dictionary<TKey, TElement> ToDictionaryIgnoreDup<TSource, TKey, TElement>
        (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer = null)
    {
        if (keySelector == null)
            throw new ArgumentNullException(nameof(keySelector));
        if (elementSelector == null)
            throw new ArgumentNullException(nameof(elementSelector));
        var d = new Dictionary<TKey, TElement>(comparer ?? EqualityComparer<TKey>.Default);
        foreach (var element in source)
            d[keySelector(element)] = elementSelector(element);
        return d;
    }

क्योंकि अनुक्रमणिका पर एक सेट इसे कुंजी जोड़ने का कारण बनता है, यह फेंक नहीं देगा, और केवल एक कुंजी लुकअप भी करेगा। आप इसे एक IEqualityComparerउदाहरण के लिए भी दे सकते हैंStringComparer.OrdinalIgnoreCase


0

कैर्रा के समाधान से शुरू करके आप इसे इस प्रकार भी लिख सकते हैं:

foreach(var person in personList.Where(el => !myDictionary.ContainsKey(el.FirstAndLastName)))
{
    myDictionary.Add(person.FirstAndLastName, person);
}

3
ऐसा नहीं है कि कोई भी कभी भी इसका उपयोग करने का प्रयास नहीं करेगा, लेकिन इसका उपयोग करने का प्रयास न करें। संग्रह को संशोधित करना जैसा कि आप उन्हें बता रहे हैं कि यह एक बुरा विचार है।
किडमोसी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.