c# - LINQ की विशिष्ट() एक विशेष संपत्ति पर




.net-3.5 distinct (12)

यदि मैं एक या अधिक गुणों के आधार पर एक अलग सूची प्राप्त करना चाहता हूं तो क्या होगा?

सरल! आप उन्हें समूहित करना चाहते हैं और समूह से बाहर विजेता चुनना चाहते हैं।

List<Person> distinctPeople = allPeople
  .GroupBy(p => p.PersonId)
  .Select(g => g.First())
  .ToList();

यदि आप कई गुणों पर समूहों को परिभाषित करना चाहते हैं, तो यहां बताया गया है कि कैसे:

List<Person> distinctPeople = allPeople
  .GroupBy(p => new {p.PersonId, p.FavoriteColor} )
  .Select(g => g.First())
  .ToList();

मैं इसके बारे में जानने के लिए LINQ के साथ खेल रहा हूं, लेकिन जब मैं एक साधारण सूची नहीं है तो मैं अलग-अलग उपयोग का उपयोग कैसे कर सकता हूं (पूर्णांक की एक साधारण सूची करना बहुत आसान है, यह सवाल नहीं है)। ऑब्जेक्ट के एक या अधिक गुणों पर ऑब्जेक्ट की सूची पर Distinct का उपयोग करना चाहते हैं तो क्या होगा?

उदाहरण: यदि संपत्ति Id साथ कोई ऑब्जेक्ट Person । मैं ऑब्जेक्ट की संपत्ति Id के साथ सभी व्यक्तियों को कैसे प्राप्त कर सकता हूं और उन पर Distinct उपयोग कैसे कर सकता हूं?

Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"

मैं सिर्फ व्यक्ति 1 और व्यक्ति 3 कैसे प्राप्त कर सकता हूं? क्या यह संभव है?

यदि LINQ के साथ यह संभव नहीं है, तो .NET 3.5 में इसके कुछ गुणों के आधार पर Person सूची रखने का सबसे अच्छा तरीका क्या होगा?


अपने फ़ील्ड द्वारा पहले समूह को समाधान करें, फिर फर्स्टऑर्डफॉल्ट आइटम का चयन करें।

    List<Person> distinctPeople = allPeople
   .GroupBy(p => p.PersonId)
   .Select(g => g.FirstOrDefault())
   .ToList();

आप इसे मानक Linq.ToLookup() साथ कर सकते हैं। यह प्रत्येक अद्वितीय कुंजी के लिए मानों का संग्रह बनाएगा। बस संग्रह में पहली आइटम का चयन करें

Persons.ToLookup(p => p.Id).Select(coll => coll.First());

आप व्यक्ति पर समान रूप से बराबर करने के लिए व्यक्ति पर समानता को ओवरराइड करने में सक्षम होना चाहिए। इसके परिणामस्वरूप आप जिस व्यवहार के बाद हैं उसका परिणाम होना चाहिए।


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


जब हमें हमारी परियोजना में इस तरह के एक कार्य का सामना करना पड़ा तो हमने तुलनित्रों को लिखने के लिए एक छोटी सी एपीआई परिभाषित की।

तो, उपयोग का मामला इस तरह था:

var wordComparer = KeyEqualityComparer.Null<Word>().
    ThenBy(item => item.Text).
    ThenBy(item => item.LangID);
...
source.Select(...).Distinct(wordComparer);

और एपीआई स्वयं इस तरह दिखता है:

using System;
using System.Collections;
using System.Collections.Generic;

public static class KeyEqualityComparer
{
    public static IEqualityComparer<T> Null<T>()
    {
        return null;
    }

    public static IEqualityComparer<T> EqualityComparerBy<T, K>(
        this IEnumerable<T> source,
        Func<T, K> keyFunc)
    {
        return new KeyEqualityComparer<T, K>(keyFunc);
    }

    public static KeyEqualityComparer<T, K> ThenBy<T, K>(
        this IEqualityComparer<T> equalityComparer,
        Func<T, K> keyFunc)
    {
        return new KeyEqualityComparer<T, K>(keyFunc, equalityComparer);
    }
}

public struct KeyEqualityComparer<T, K>: IEqualityComparer<T>
{
    public KeyEqualityComparer(
        Func<T, K> keyFunc,
        IEqualityComparer<T> equalityComparer = null)
    {
        KeyFunc = keyFunc;
        EqualityComparer = equalityComparer;
    }

    public bool Equals(T x, T y)
    {
        return ((EqualityComparer == null) || EqualityComparer.Equals(x, y)) &&
                EqualityComparer<K>.Default.Equals(KeyFunc(x), KeyFunc(y));
    }

    public int GetHashCode(T obj)
    {
        var hash = EqualityComparer<K>.Default.GetHashCode(KeyFunc(obj));

        if (EqualityComparer != null)
        {
            var hash2 = EqualityComparer.GetHashCode(obj);

            hash ^= (hash2 << 5) + hash2;
        }

        return hash;
    }

    public readonly Func<T, K> KeyFunc;
    public readonly IEqualityComparer<T> EqualityComparer;
}

अधिक जानकारी हमारे साइट पर है: LINQ में IEqualityComparer


मुझे लगता है कि यह पर्याप्त है:

list.Select(s => s.MyField).Distinct();

मैंने एक लेख लिखा है जो बताता है कि विशिष्ट कार्य को कैसे विस्तारित किया जाए ताकि आप निम्नानुसार कार्य कर सकें:

var people = new List<Person>();

people.Add(new Person(1, "a", "b"));
people.Add(new Person(2, "c", "d"));
people.Add(new Person(1, "a", "b"));

foreach (var person in people.Distinct(p => p.ID))
    // Do stuff with unique list here.

यहां लेख है: LINQ का विस्तार - विशिष्ट कार्य में एक संपत्ति निर्दिष्ट करना


यदि आप सभी LINQ-like को देखना चाहते हैं तो आप क्वेरी सिंटैक्स का भी उपयोग कर सकते हैं:

var uniquePeople = from p in people
                   group p by new {p.ID} //or group by new {p.ID, p.Name, p.Whatever}
                   into mygroup
                   select mygroup.FirstOrDefault();

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

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

using PowerfulExtensions.Linq;
...
var distinct = myArray.Distinct(x => x.A, x => x.B);

संपादित करें : यह अब MoreLINQ हिस्सा है।

आपको जो चाहिए वह प्रभावी रूप से "अलग-अलग" है। मुझे विश्वास नहीं है कि यह LINQ का हिस्सा है क्योंकि यह खड़ा है, हालांकि यह लिखना काफी आसान है:

public static IEnumerable<TSource> DistinctBy<TSource, TKey>
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> seenKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        if (seenKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}

तो केवल Id संपत्ति का उपयोग करके अलग-अलग मानों को ढूंढने के लिए, आप इसका उपयोग कर सकते हैं:

var query = people.DistinctBy(p => p.Id);

और एकाधिक गुणों का उपयोग करने के लिए, आप अनाम प्रकारों का उपयोग कर सकते हैं, जो समानता को उचित रूप से कार्यान्वित करते हैं:

var query = people.DistinctBy(p => new { p.Id, p.Name });

अनचाहे, लेकिन यह काम करना चाहिए (और अब यह कम से कम संकलित)।

हालांकि यह चाबियों के लिए डिफ़ॉल्ट तुलनाकर्ता मानता है - यदि आप समानता तुलनाकर्ता में पास करना चाहते हैं, तो बस इसे HashSet कन्स्ट्रक्टर पर पास करें।


List<Person>lst=new List<Person>
        var result1 = lst.OrderByDescending(a => a.ID).Select(a =>new Player {ID=a.ID,Name=a.Name} ).Distinct();




distinct