c# - متميزة() مع لامدا؟




c#-3.0 lambda (12)

صحيح ، لذلك لدي عدد لا يحصى ، وأتمنى الحصول على قيم مميزة منه.

باستخدام System.Linq ، هناك بالطبع طريقة ملحق تسمى Distinct . في الحالة البسيطة ، يمكن استخدامه بدون أي معلمات ، مثل:

var distinctValues = myStringList.Distinct();

حسنًا وجيدًا ، ولكن إذا كان لدي عدد لا يحصى من الكائنات التي أحتاج إلى تحديد المساواة فيها ، فإن الحمل الزائد الوحيد المتاح هو:

var distinctValues = myCustomerList.Distinct(someEqualityComparer);

يجب أن تكون الوسيطة IEqualityComparer<T> comparer مثال IEqualityComparer<T> . يمكنني أن أفعل ذلك بالطبع ، لكنه مطوّل إلى حدٍّ ما ، وهو جيد التباين.

ما كنت أتوقعه هو الحمل الزائد الذي سيأخذ أمدا ، على سبيل المثال Func <T، T، bool>:

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

أي شخص يعرف ما إذا كان هناك بعض التمديد موجود ، أو بعض الحل البديل؟ أم هل فاتني شيء؟

بدلاً من ذلك ، هل هناك طريقة لتحديد IEqualityComparer مضمنة (embarass me)؟

تحديث

عثرت على رد من Anders Hejlsberg على post في منتدى MSDN حول هذا الموضوع. هو يقول:

المشكلة التي ستقوم بتشغيلها هي أنه عند مقارنة جسمين متساويين يجب أن يكون لهما نفس قيمة إرجاع GetHashCode (وإلا لن يعمل جدول التجزئة المستخدم داخليًا بواسطة Distinct بشكل صحيح). نحن نستخدم IEqualityComparer لأنه يحزم تطبيقات متوافقة من Equals و GetHashCode في واجهة واحدة.

أعتقد أن من المنطقي..


أفترض أن لديك IEnumerable ، وفي مثالك المفوض ، هل ترغب في أن يكون c1 و c2 يشيران إلى عنصرين في هذه القائمة؟

أعتقد أنه يمكنك تحقيق ذلك باستخدام self join var distinctResults = من c1 في myList join c2 في myList on


إذا لم تؤد 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);

تحتوي حزمة Microsoft System.Interactive على إصدار متميز يتخذه lambda محدد المفتاح. هذا هو نفس حل Jon Skeet ، ولكن قد يكون من المفيد للأشخاص أن يعرفوا ، وللتحقق من بقية المكتبة.


تعتمد كل الحلول التي رأيتها هنا على اختيار مجال قابل للمقارنة بالفعل. إذا كان المرء بحاجة إلى المقارنة بطريقة مختلفة ، فإن هذا الحل هنا يبدو أنه يعمل بشكل عام ، من أجل شيء مثل:

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

خذ طريقة أخرى:

var distinctValues = myCustomerList.
Select(x => x._myCaustomerProperty).Distinct();

يعرض التسلسل عناصر مميزة تقارن بينها بواسطة الخاصية '_myCoustomerProperty'.


شيء كنت قد استخدمت والتي عملت بشكل جيد بالنسبة لي.

/// <summary>
/// A class to wrap the IEqualityComparer interface into matching functions for simple implementation
/// </summary>
/// <typeparam name="T">The type of object to be compared</typeparam>
public class MyIEqualityComparer<T> : IEqualityComparer<T>
{
    /// <summary>
    /// Create a new comparer based on the given Equals and GetHashCode methods
    /// </summary>
    /// <param name="equals">The method to compute equals of two T instances</param>
    /// <param name="getHashCode">The method to compute a hashcode for a T instance</param>
    public MyIEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
    {
        if (equals == null)
            throw new ArgumentNullException("equals", "Equals parameter is required for all MyIEqualityComparer instances");
        EqualsMethod = equals;
        GetHashCodeMethod = getHashCode;
    }
    /// <summary>
    /// Gets the method used to compute equals
    /// </summary>
    public Func<T, T, bool> EqualsMethod { get; private set; }
    /// <summary>
    /// Gets the method used to compute a hash code
    /// </summary>
    public Func<T, int> GetHashCodeMethod { get; private set; }

    bool IEqualityComparer<T>.Equals(T x, T y)
    {
        return EqualsMethod(x, y);
    }

    int IEqualityComparer<T>.GetHashCode(T obj)
    {
        if (GetHashCodeMethod == null)
            return obj.GetHashCode();
        return GetHashCodeMethod(obj);
    }
}

لا لا يوجد مثل هذا الزائد طريقة تمديد لهذا. لقد وجدت هذا محبطًا نفسيًا في الماضي وعلى هذا النحو عادةً ما أكتب صفًا مساعدًا للتعامل مع هذه المشكلة. الهدف هو تحويل 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));

ملحق لامدا IEnumerable :

public static class ListExtensions
{        
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, int> hashCode)
    {
        Dictionary<int, T> hashCodeDic = new Dictionary<int, T>();

        list.ToList().ForEach(t => 
            {   
                var key = hashCode(t);
                if (!hashCodeDic.ContainsKey(key))
                    hashCodeDic.Add(key, t);
            });

        return hashCodeDic.Select(kvp => kvp.Value);
    }
}

الاستعمال:

class Employee
{
    public string Name { get; set; }
    public int EmployeeID { get; set; }
}

//Add 5 employees to List
List<Employee> lst = new List<Employee>();

Employee e = new Employee { Name = "Shantanu", EmployeeID = 123456 };
lst.Add(e);
lst.Add(e);

Employee e1 = new Employee { Name = "Adam Warren", EmployeeID = 823456 };
lst.Add(e1);
//Add a space in the Name
Employee e2 = new Employee { Name = "Adam  Warren", EmployeeID = 823456 };
lst.Add(e2);
//Name is different case
Employee e3 = new Employee { Name = "adam warren", EmployeeID = 823456 };
lst.Add(e3);            

//Distinct (without IEqalityComparer<T>) - Returns 4 employees
var lstDistinct1 = lst.Distinct();

//Lambda Extension - Return 2 employees
var lstDistinct = lst.Distinct(employee => employee.EmployeeID.GetHashCode() ^ employee.Name.ToUpper().Replace(" ", "").GetHashCode()); 

والطريقة الصعبة للقيام بذلك هي استخدام امتداد 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());

يبدو لي مثلك تريد DistinctBy من MoreLINQ . يمكنك بعد ذلك الكتابة:

var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId);

وإليك إصدارًا مقطوعًا من DistinctBy (لا يتم التحقق من أي بطلان ولا يوجد خيار لتحديد مقارنتك الخاصة):

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

لإنهاء الامور . أعتقد أن معظم الأشخاص الذين جاءوا إلى هنا مثلي يريدون أبسط حل ممكن دون استخدام أي مكتبات وأفضل أداء ممكن.

(أعتقد أن المجموعة المقبولة حسب الأسلوب بالنسبة لي هي مبالغة في الأداء).

إليك طريقة تمديد بسيطة باستخدام واجهة 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