LINQ: विशिष्ट मान


136

मेरे पास XML से निम्न आइटम सेट है:

id           category

5            1
5            3
5            4
5            3
5            3

मुझे इन वस्तुओं की एक अलग सूची चाहिए:

5            1
5            3
5            4

मैं LINQ में श्रेणी और आईडी के लिए भी अलग कैसे हो सकता हूं?

जवाबों:


221

क्या आप एक से अधिक क्षेत्रों से अलग होने की कोशिश कर रहे हैं? यदि ऐसा है, तो बस एक गुमनाम प्रकार और डिस्टिक्ट ऑपरेटर का उपयोग करें और यह ठीक होना चाहिए:

var query = doc.Elements("whatever")
               .Select(element => new {
                             id = (int) element.Attribute("id"),
                             category = (int) element.Attribute("cat") })
               .Distinct();

यदि आप "बड़े" प्रकार के मूल्यों का एक अलग सेट प्राप्त करने की कोशिश कर रहे हैं, लेकिन केवल विशिष्ट पहलू के लिए गुणों के कुछ सबसेट को देख रहे हैं, तो आप संभवतः MoreLINQ में इसेDistinctBy लागू करना चाहते हैं :DistinctBy.cs

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

(यदि आप nullतुलनाकर्ता के रूप में पास होते हैं , तो यह कुंजी प्रकार के लिए डिफ़ॉल्ट तुलना का उपयोग करेगा।)


ओह, तो "बड़े प्रकार" से आपका मतलब हो सकता है कि मैं अभी भी परिणाम में सभी गुण चाहता हूं, हालांकि मैं केवल विशिष्टता को निर्धारित करने के लिए कुछ गुणों की तुलना करना चाहता हूं?
लाल मटर

@ TheRedPea: हाँ, बिल्कुल।
जॉन स्कीट


27

जॉन स्कीट के जवाब के अलावा, आप समूह का उपयोग अभिव्यक्ति के अनूठे समूहों के साथ w / प्रत्येक समूह पुनरावृत्तियों के लिए एक गिनती प्राप्त करने के लिए भी कर सकते हैं:

var query = from e in doc.Elements("whatever")
            group e by new { id = e.Key, val = e.Value } into g
            select new { id = g.Key.id, val = g.Key.val, count = g.Count() };

4
आपने लिखा "जॉन स्कीट के जवाब के अलावा" ... मुझे नहीं पता कि क्या ऐसा संभव है। ;)
येहुदा

13

अभी भी देख रहे किसी के लिए; यहाँ एक कस्टम लैम्डा तुलनित्र को लागू करने का एक और तरीका है।

public class LambdaComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> _expression;

        public LambdaComparer(Func<T, T, bool> lambda)
        {
            _expression = lambda;
        }

        public bool Equals(T x, T y)
        {
            return _expression(x, y);
        }

        public int GetHashCode(T obj)
        {
            /*
             If you just return 0 for the hash the Equals comparer will kick in. 
             The underlying evaluation checks the hash and then short circuits the evaluation if it is false.
             Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects), 
             you will always fall through to the Equals check which is what we are always going for.
            */
            return 0;
        }
    }

फिर आप लैंब डिस्टिंच के लिए एक एक्सटेंशन बना सकते हैं जो लैम्ब्डा में ले जा सकता है

   public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list,  Func<T, T, bool> lambda)
        {
            return list.Distinct(new LambdaComparer<T>(lambda));
        }  

उपयोग:

var availableItems = list.Distinct((p, p1) => p.Id== p1.Id);

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

अच्छी बात! मैं समय मिलने पर संपादित करने का प्रयास करूंगा, यदि आप इस समय इस डोमेन में काम कर रहे हैं, तो संपादित करने के लिए स्वतंत्र महसूस करें
रिकी जी

8

मुझे उत्तर देने में थोड़ी देर हो गई है, लेकिन आप ऐसा करना चाहते हैं यदि आप संपूर्ण तत्व चाहते हैं, न केवल वे मान जिन्हें आप समूह में रखना चाहते हैं:

var query = doc.Elements("whatever")
               .GroupBy(element => new {
                             id = (int) element.Attribute("id"),
                             category = (int) element.Attribute("cat") })
               .Select(e => e.First());

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


4
// First Get DataTable as dt
// DataRowComparer Compare columns numbers in each row & data in each row

IEnumerable<DataRow> Distinct = dt.AsEnumerable().Distinct(DataRowComparer.Default);

foreach (DataRow row in Distinct)
{
    Console.WriteLine("{0,-15} {1,-15}",
        row.Field<int>(0),
        row.Field<string>(1)); 
}

0

चूंकि हम हर तत्व के एक बार होने की बात कर रहे हैं, इसलिए एक "सेट" मेरे लिए अधिक मायने रखता है।

कक्षाओं और IEqualityComparer के साथ उदाहरण लागू किया गया:

 public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public Product(int x, string y)
        {
            Id = x;
            Name = y;
        }
    }

    public class ProductCompare : IEqualityComparer<Product>
    {
        public bool Equals(Product x, Product y)
        {  //Check whether the compared objects reference the same data.
            if (Object.ReferenceEquals(x, y)) return true;

            //Check whether any of the compared objects is null.
            if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
                return false;

            //Check whether the products' properties are equal.
            return x.Id == y.Id && x.Name == y.Name;
        }
        public int GetHashCode(Product product)
        {
            //Check whether the object is null
            if (Object.ReferenceEquals(product, null)) return 0;

            //Get hash code for the Name field if it is not null.
            int hashProductName = product.Name == null ? 0 : product.Name.GetHashCode();

            //Get hash code for the Code field.
            int hashProductCode = product.Id.GetHashCode();

            //Calculate the hash code for the product.
            return hashProductName ^ hashProductCode;
        }
    }

अभी

List<Product> originalList = new List<Product> {new Product(1, "ad"), new Product(1, "ad")};
var setList = new HashSet<Product>(originalList, new ProductCompare()).ToList();

setList अद्वितीय तत्व होंगे

मैंने इसके बारे में सोचा था, .Except()जिसके साथ एक सेट-अंतर देता है

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