c# - लैम्ब्डा के साथ अलग()?




c#-3.0 lambda (12)

ठीक है, तो मेरे पास एक संख्यात्मक है और इससे अलग मूल्य प्राप्त करना चाहते हैं।

System.Linq का उपयोग करना, निश्चित रूप से एक विस्तार विधि कहा जाता है जिसे Distinct कहा जाता है। साधारण मामले में, इसका उपयोग किसी पैरामीटर के साथ नहीं किया जा सकता है, जैसे:

var distinctValues = myStringList.Distinct();

अच्छा और अच्छा, लेकिन अगर मेरे पास ऐसी वस्तुओं का एक अनुमान है जिसके लिए मुझे समानता निर्दिष्ट करने की आवश्यकता है, तो केवल उपलब्ध अधिभार ही है:

var distinctValues = myCustomerList.Distinct(someEqualityComparer);

समानता तुलनाकर्ता तर्क IEqualityComparer<T> का एक उदाहरण होना चाहिए। मैं यह निश्चित रूप से कर सकता हूं, लेकिन यह कुछ हद तक verbose और, अच्छी तरह से, cludgy है।

मैं जो अपेक्षा करता था वह एक अधिभार है जो एक लैम्ब्डा लेता है, एक Func <टी, टी, बूल> कहें:

var distinctValues
    = myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);

किसी को पता है कि कुछ ऐसे एक्सटेंशन मौजूद हैं, या कुछ समकक्ष कामकाज? या क्या मैं कुछ न कुछ भूल रहा हूं?

वैकल्पिक रूप से, क्या एक IEqualityComparer इनलाइन निर्दिष्ट करने का कोई तरीका है (मुझे शर्मिंदा करें)?

अद्यतन करें

मुझे एंडर्स हेजल्सबर्ग ने इस विषय पर एक एमएसडीएन मंच में एक post लिए एक post दिया। वह कहता है:

जिस समस्या में आप भागने जा रहे हैं वह यह है कि जब दो ऑब्जेक्ट बराबर की तुलना करते हैं तो उनके पास गेटहैशकोड रिटर्न वैल्यू होना चाहिए (या फिर डिस्टिंट द्वारा आंतरिक रूप से उपयोग की गई हैश तालिका सही ढंग से काम नहीं करेगी)। हम IEqualityComparer का उपयोग करते हैं क्योंकि यह समान इंटरफ़ेस में समान और GetHashCode के संगत कार्यान्वयन पैकेज करता है।

मुझे लगता है कि समझ में आता है ..


आप इनलाइन कॉम्पैयर का उपयोग कर सकते हैं

public class InlineComparer<T> : IEqualityComparer<T>
{
    //private readonly Func<T, T, bool> equalsMethod;
    //private readonly Func<T, int> getHashCodeMethod;
    public Func<T, T, bool> EqualsMethod { get; private set; }
    public Func<T, int> GetHashCodeMethod { get; private set; }

    public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        if (equals == null) throw new ArgumentNullException("equals", "Equals parameter is required for all InlineComparer instances");
        EqualsMethod = equals;
        GetHashCodeMethod = hashCode;
    }

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

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

उपयोग नमूना :

  var comparer = new InlineComparer<DetalleLog>((i1, i2) => i1.PeticionEV == i2.PeticionEV && i1.Etiqueta == i2.Etiqueta, i => i.PeticionEV.GetHashCode() + i.Etiqueta.GetHashCode());
  var peticionesEV = listaLogs.Distinct(comparer).ToList();
  Assert.IsNotNull(peticionesEV);
  Assert.AreNotEqual(0, peticionesEV.Count);

स्रोत: https://.com/a/5969691/206730
संघ के लिए IEqualityComparer का उपयोग करना
क्या मैं अपना स्पष्ट प्रकार तुलनित्र इनलाइन निर्दिष्ट कर सकता हूं?


इसके लिए कोई एक्सटेंशन विधि ओवरलोड नहीं है। मैंने खुद को अतीत में निराशाजनक पाया है और इस तरह मैं आमतौर पर इस समस्या से निपटने के लिए एक सहायक वर्ग लिखता हूं। लक्ष्य एक Func<T,T,bool> को IEqualityComparer<T,T> परिवर्तित करना है।

उदाहरण

public class EqualityFactory {
  private sealed class Impl<T> : IEqualityComparer<T,T> {
    private Func<T,T,bool> m_del;
    private IEqualityComparer<T> m_comp;
    public Impl(Func<T,T,bool> del) { 
      m_del = del;
      m_comp = EqualityComparer<T>.Default;
    }
    public bool Equals(T left, T right) {
      return m_del(left, right);
    } 
    public int GetHashCode(T value) {
      return m_comp.GetHashCode(value);
    }
  }
  public static IEqualityComparer<T,T> Create<T>(Func<T,T,bool> del) {
    return new Impl<T>(del);
  }
}

यह आपको निम्नलिखित लिखने की अनुमति देता है

var distinctValues = myCustomerList
  .Distinct(EqualityFactory.Create((c1, c2) => c1.CustomerId == c2.CustomerId));

ऐसा करने का एक मुश्किल तरीका Aggregate() एक्सटेंशन का उपयोग करता है, कुंजी-प्रॉपर्टी मानों के साथ संचयक के रूप में एक शब्दकोश का उपयोग कुंजी के रूप में होता है:

var customers = new List<Customer>();

var distincts = customers.Aggregate(new Dictionary<int, Customer>(), 
                                    (d, e) => { d[e.CustomerId] = e; return d; },
                                    d => d.Values);

और समूह-स्टाइल समाधान ToLookup() का उपयोग कर रहा है:

var distincts = customers.ToLookup(c => c.CustomerId).Select(g => g.First());

मुझे लगता है कि आपके पास एक आईनेमरेबल है, और आपके उदाहरण प्रतिनिधि में, आप सी 1 और सी 2 को इस सूची में दो तत्वों का जिक्र करना चाहते हैं?

मेरा मानना ​​है कि आप इसे स्वयं से प्राप्त कर सकते हैं var अलग-अलग = c1 से myList में myList में c2 में शामिल हों


मैंने देखा है कि सभी समाधान पहले से ही तुलनात्मक क्षेत्र का चयन करने पर भरोसा करते हैं। अगर किसी को अलग तरीके से तुलना करने की ज़रूरत है, हालांकि, यहां यह समाधान आम तौर पर काम करने लगता है, जैसे कुछ:

somedoubles.Distinct(new LambdaComparer<double>((x, y) => Math.Abs(x - y) < double.Epsilon)).Count()

यदि Distinct() अद्वितीय परिणाम नहीं देता है, तो इसे आजमाएं:

var filteredWC = tblWorkCenter.GroupBy(cc => cc.WCID_I).Select(grp => grp.First()).Select(cc => new Model.WorkCenter { WCID = cc.WCID_I }).OrderBy(cc => cc.WCID); 

ObservableCollection<Model.WorkCenter> WorkCenter = new ObservableCollection<Model.WorkCenter>(filteredWC);

यह वही करेगा जो आप चाहते हैं लेकिन मुझे प्रदर्शन के बारे में पता नहीं है:

var distinctValues =
    from cust in myCustomerList
    group cust by cust.CustomerId
    into gcust
    select gcust.First();

कम से कम यह verbose नहीं है।


यहां एक साधारण विस्तार विधि है जो मुझे चाहिए ...

public static class EnumerableExtensions
{
    public static IEnumerable<TKey> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector)
    {
        return source.GroupBy(selector).Select(x => x.Key);
    }
}

यह एक शर्म की बात है कि उन्होंने फ्रेमवर्क में इस तरह की एक अलग विधि नहीं बनाई, लेकिन हे हो।


शॉर्टैंड समाधान

myCustomerList.GroupBy(c => c.CustomerId, (key, c) => c.FirstOrDefault());

माइक्रोसॉफ्ट सिस्टमइंटरैक्टिव पैकेज में डिस्टिंट का एक संस्करण है जो एक प्रमुख चयनकर्ता लैम्ब्डा लेता है। यह प्रभावी रूप से जॉन स्कीट के समाधान जैसा ही है, लेकिन लोगों के बारे में जानना और पुस्तकालय के बाकी हिस्सों की जांच करना सहायक हो सकता है।


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

(मेरे लिए विधि द्वारा स्वीकृत समूह मुझे लगता है कि प्रदर्शन के मामले में एक ओवरकिल है।)

IEqualityComparer इंटरफ़ेस का उपयोग करके एक सरल एक्सटेंशन विधि यहां दी गई है जो शून्य मानों के लिए भी काम करती है।

उपयोग:

var filtered = taskList.DistinctBy(t => t.TaskExternalId).ToArray();

विस्तार विधि कोड

public static class LinqExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
    {
        GeneralPropertyComparer<T, TKey> comparer = new GeneralPropertyComparer<T,TKey>(property);
        return items.Distinct(comparer);
    }   
}
public class GeneralPropertyComparer<T,TKey> : IEqualityComparer<T>
{
    private Func<T, TKey> expr { get; set; }
    public GeneralPropertyComparer (Func<T, TKey> expr)
    {
        this.expr = expr;
    }
    public bool Equals(T left, T right)
    {
        var leftProp = expr.Invoke(left);
        var rightProp = expr.Invoke(right);
        if (leftProp == null && rightProp == null)
            return true;
        else if (leftProp == null ^ rightProp == null)
            return false;
        else
            return leftProp.Equals(rightProp);
    }
    public int GetHashCode(T obj)
    {
        var prop = expr.Invoke(obj);
        return (prop==null)? 0:prop.GetHashCode();
    }
}

IEnumerable<Customer> filteredList = originalList
  .GroupBy(customer => customer.CustomerId)
  .Select(group => group.First());




extension-methods