LINQ बेनामी प्रकारों के साथ डिस्टिक्ट का चयन करें


150

इसलिए मेरे पास वस्तुओं का एक संग्रह है। सटीक प्रकार महत्वपूर्ण नहीं है। इससे मैं विशेष गुणों की एक जोड़ी के सभी अद्वितीय जोड़े निकालना चाहता हूं, इस प्रकार:

myObjectCollection.Select(item=>new
                                {
                                     Alpha = item.propOne,
                                     Bravo = item.propTwo
                                }
                 ).Distinct();

तो मेरा सवाल है: क्या इस मामले में डिस्टिक्ट डिफ़ॉल्ट ऑब्जेक्ट बराबरी का उपयोग करेगा (जो कि मेरे लिए बेकार होगा, क्योंकि प्रत्येक ऑब्जेक्ट नया है) या इसे एक अलग इक्वल्स करने के लिए कहा जा सकता है (इस मामले में, अल्फा और ब्रावो के बराबर मान => समान उदाहरण)? क्या उस परिणाम को प्राप्त करने का कोई तरीका है, अगर यह ऐसा नहीं करता है?


क्या यह LINQ-to-Objects या LINQ-to-SQL है? अगर सिर्फ वस्तुओं, तुम शायद भाग्य से बाहर हो। हालाँकि, यदि L2S, तो यह काम कर सकता है, क्योंकि DISTINCT को SQL स्टेटमेंट पर पास किया जाएगा।
जेम्स कर्रान

जवाबों:


188

K. स्कॉट एलन की उत्कृष्ट पोस्ट के माध्यम से यहाँ पढ़ें:

और सभी के लिए समानता ... अनाम प्रकार

संक्षिप्त उत्तर (और मैं बोली):

अनाम प्रकारों के लिए C # संकलक समानताओं और GetHashCode को ओवरराइड करता है। दो ओवरराइड विधियों का कार्यान्वयन ऑब्जेक्ट के हैश कोड की गणना करने और समानता के लिए परीक्षण करने के लिए सभी सार्वजनिक गुणों का उपयोग करता है। यदि एक ही अनाम प्रकार की दो वस्तुओं में उनके गुणों के सभी मान समान हैं - तो वस्तुएं समान हैं।

तो अनाम प्रकारों को लौटाने वाली क्वेरी पर Distinct () पद्धति का उपयोग करना पूरी तरह से सुरक्षित है।


2
यह केवल सच है, मुझे लगता है, यदि गुण स्वयं मूल्य प्रकार हैं या मूल्य समानता को लागू करते हैं - मेरा जवाब देखें।
tvanfosson

हां, चूंकि यह प्रत्येक संपत्ति पर GetHashCode का उपयोग करता है तो यह केवल तभी काम करेगा जब प्रत्येक संपत्ति का अपना अनूठा कार्यान्वयन हो। मुझे लगता है कि अधिकांश उपयोग-मामलों में केवल साधारण प्रकार के गुण शामिल होंगे ताकि यह आम तौर पर सुरक्षित हो।
मैट हैमिल्टन

4
इसका अर्थ यह है कि दो प्रकार के अनाम प्रकारों की समानता सदस्यों की समानता पर निर्भर करती है, जो कि मेरे द्वारा ठीक है, क्योंकि सदस्यों को कहीं न कहीं परिभाषित किया जाता है और यदि मुझे करना है तो मैं समानता को समाप्त कर सकता हूं। मैं बस इसके लिए एक वर्ग बनाने के लिए नहीं करना चाहता था कि यह बराबरी से आगे निकल जाए।
GWLlosa

3
एमएस को "कुंजी" वाक्यविन्यास को C # में लाने के लिए याचिका दायर करने के लायक हो सकता है, जिसमें VB है (जहां आप एक गुमनाम प्रकार के कुछ गुणों को 'प्राथमिक कुंजी' के रूप में निर्दिष्ट कर सकते हैं - वह ब्लॉग पोस्ट देखें जिसे मैंने लिंक किया था)।
मैट हैमिल्टन

1
बहुत ही रोचक लेख। धन्यवाद!
अलेक्जेंडर प्रोकोफ़ेव

14
public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();

पहले से गड़बड़ प्रारूप के लिए क्षमा करें


यह एक्सटेंशन प्रकार की objectऔर संभाल नहीं सकता है object। यदि यह दोनों objectहै stringतो भी डुप्लिकेट पंक्तियाँ वापस करें। FirstNameटाइपोफ़ की कोशिश करें objectऔर उसी के साथ असाइन करें string
CallMeLaNN

यह टाइप की गई वस्तुओं के लिए एक शानदार उत्तर है लेकिन अनाम प्रकारों के लिए आवश्यक नहीं है।
क्रोकेक

5

दिलचस्प है कि यह सी # में काम करता है लेकिन वीबी में नहीं

26 पत्र लौटाता है:

var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
MyBet.ToCharArray()
.Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()})
.Distinct()
.Dump();

52 देता है ...

Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
MyBet.ToCharArray() _
.Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _
.Distinct() _
.Dump()

11
यदि आप Keyकीवर्ड को अनाम प्रकार में जोड़ते हैं तो इच्छानुसार .Distinct()काम करेगा (उदाहरण के लिए New With { Key .lower = x.ToString.ToLower(), Key .upper = x.ToString.ToUpper()})।
C

3
Cory सही है। C # कोड new {A = b}का सही अनुवाद है New {Key .A = b}। अनाम VB वर्गों में गैर-प्रमुख गुण परस्पर भिन्न होते हैं, यही वजह है कि उनकी तुलना संदर्भ से की जाती है। C # में, अनाम वर्गों के सभी गुण अपरिवर्तनीय हैं।
हीनज़ी

4

मैंने थोड़ा परीक्षण किया और पाया कि यदि गुण मान प्रकार हैं, तो यह ठीक काम करता है। यदि वे मूल्य प्रकार नहीं हैं, तो यह आवश्यक है कि यह स्वयं के बराबर हो और इसके लिए काम करने के लिए GetHashCode कार्यान्वयन हो। स्ट्रिंग्स, मुझे लगता है, काम करेगा।


2

आप अपनी खुद की डिस्टि्रक्ट एक्सटेंशन विधि बना सकते हैं जो लैम्ब्डा एक्सप्रेशन लेती है। यहाँ एक उदाहरण है

एक वर्ग बनाएं जो IEqualityComparer इंटरफ़ेस से प्राप्त होता है

public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

फिर अपनी डिस्टिक्ट एक्सटेंशन विधि बनाएं

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

और आप इस विधि का उपयोग कर सकते हैं अलग आइटम खोजने के लिए

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();

यह एक्सटेंशन प्रकार की objectऔर संभाल नहीं सकता है object। यदि यह दोनों objectहै stringतो भी डुप्लिकेट पंक्तियाँ वापस करें। FirstNameटाइपोफ़ की कोशिश करें objectऔर उसी के साथ असाइन करें string
CallMeLaNN

0

यदि Alphaऔर Bravoदोनों एक सामान्य वर्ग से विरासत में मिलते हैं, तो आप लागू करके माता-पिता वर्ग में समानता की जांच करने में सक्षम होंगे IEquatable<T>

उदाहरण के लिए:

public class CommonClass : IEquatable<CommonClass>
{
    // needed for Distinct()
    public override int GetHashCode() 
    {
        return base.GetHashCode();
    }

    public bool Equals(CommonClass other)
    {
        if (other == null) return false;
        return [equality test];
    }
}

इसलिए यदि आप अपने गुमनाम प्रकार वर्गों के गुणों के रूप में उपयोग करते हैं जो IEquatable <T> को लागू करता है, तो डिफ़ॉल्ट व्यवहार (प्रतिबिंब के माध्यम से सभी सार्वजनिक गुणों की जांच?) के बजाय बराबर कहा जाता है
D_Guidi

0

अरे वहाँ मैं एक ही समस्या है और मैं एक समाधान मिल गया। आपको IEquatable इंटरफ़ेस लागू करना होगा या बस (बराबर और GetHashCode) विधियों को ओवरराइड करना होगा। लेकिन यह ट्रिक नहीं है, GetHashCode Method में आने वाली ट्रिक। आपको अपनी कक्षा के ऑब्जेक्ट का हैश कोड वापस नहीं करना चाहिए, लेकिन आपको उस संपत्ति का हैश लौटना चाहिए, जिसकी आप तुलना करना चाहते हैं।

public override bool Equals(object obj)
    {
        Person p = obj as Person;
        if ( obj == null )
            return false;
        if ( object.ReferenceEquals( p , this ) )
            return true;
        if ( p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian )
            return true;
        return false;
        //return base.Equals( obj );
    }
    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }

जैसा कि आप देखते हैं कि मुझे एक वर्ग मिला है जिसे व्यक्ति कहा जाता है उसे 3 गुण मिले (नाम, आयु, ईज़ीलियन "क्योंकि मैं हूं") गेटहैशकोड में मैंने नाम संपत्ति के हैश को व्यक्तिगत वस्तु नहीं लौटाया।

यह कोशिश करो और यह आईएसए काम करेगा। धन्यवाद, मोददर सादिक


1
GetHashCode को उन सभी क्षेत्रों और गुणों का उपयोग करना चाहिए जो समानता के लिए तुलना में उपयोग किए जाते हैं, न कि उनमें से केवल एक। यानीpublic override int GetHashCode() { return this.Name.GetHashCode() ^ this.Age.GetHashCode() ^ this.IsEgyptian.GetHashCode(); }
JG SD

एक अच्छा हैश एल्गोरिथ्म उत्पन्न करने के
SD

0

इसके लिए VB.NET में काम करने के लिए, आपको Keyप्रत्येक संपत्ति से पहले अनाम प्रकार में कीवर्ड निर्दिष्ट करने की आवश्यकता है :

myObjectCollection.Select(Function(item) New With
{
    Key .Alpha = item.propOne,
    Key .Bravo = item.propTwo
}).Distinct()

मैं इसके साथ संघर्ष कर रहा था, मुझे लगा कि VB.NET इस प्रकार की सुविधा का समर्थन नहीं करता है, लेकिन वास्तव में यह करता है।

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