独特()与lambda?

对,所以我有一个枚举,并希望从它得到不同的价值。

使用System.Linq ,当然有一个名为Distinct的扩展方法。 在简单情况下,它可以使用没有参数,如:

 var distinctValues = myStringList.Distinct(); 

那么好,但如果我有我需要指定相等的对象的枚举,唯一可用的重载是:

 var distinctValues = myCustomerList.Distinct(someEqualityComparer); 

相等比较器参数必须是IEqualityComparer<T>一个实例。 当然,我可以做到这一点,但是有点冗长,而且很有趣。

我所期望的是一个需要lambda的重载,比如Func <T,T,bool>:

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

任何人都知道,如果这样的扩展存在,或一些等效的解决方法? 还是我错过了什么?

另外,有没有一种方法来指定一个IEqualityComparer内联(不容我)?

更新

我发现Anders Hejlsberg在MSDN论坛上发布了一个关于这个主题的post。 他说:

你将遇到的问题是,当两个对象比较相等时,它们必须具有相同的GetHashCode返回值(否则Distinct内部使用的哈希表将无法正常工作)。 我们使用IEqualityComparer,因为它将Equals和GetHashCode的兼容实现打包到一个接口中。

我想这是有道理的..

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

它看起来像你想从MoreLINQ DistinctBy 。 然后你可以写:

 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; } } } 

不,没有这样的扩展方法过载。 过去我发现自己感到沮丧,所以我通常会写一个帮助类来处理这个问题。 目标是将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)); 

把东西包起来 。 我认为像我这样来到这里的大多数人都希望能够使用 最简单的解决scheme, 而无需使用任何图书馆和最佳性能

(对于我来说,接受的方法对我来说是一个性能上的矫枉过正。)

以下是使用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(); } } 

这将做你想要的,但我不知道性能:

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

至less它不是冗长的。

速记解决scheme

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

这是一个简单的扩展方法,做我所需要的…

 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); } } 

他们并没有将这样一个独特的方法烘焙到框架中,这是一种耻辱,但是嘿嘿。

我用过的东西对我来说效果很好。

 /// <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); } } 

我在这里看到的所有解决scheme都依赖于select一个已经可比的领域 但是,如果需要以不同的方式进行比较,那么这个解决scheme似乎总体上是工作的,例如:

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

你可以使用InlineComparer

 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 : //stackoverflow.com/a/5969691/206730
使用IEqualityComparer for Union
我可以指定我的显式types比较内联?

我假设你有一个IEnumerable,并在你的例子代表,你想c1和c2是指这个列表中的两个元素?

我相信你可以实现这个自我联结变种distinctResults =从c1在myList中joinc2在myList中

如果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包有一个Distinct版本,它需要一个密钥select器lambda。 这与Jon Skeet的解决scheme实际上是一样的,但它可能有助于人们知道,并检查图书馆的其余部分。

一个棘手的方法是使用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); 

GroupBy风格的解决scheme使用ToLookup()

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

你可以这样做:

 public static class Extensions { public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query, Func<T, V> f, Func<IGrouping<V,T>,T> h=null) { if (h==null) h=(x => x.First()); return query.GroupBy(f).Select(h); } } 

这个方法允许你通过指定一个像.MyDistinct(d => d.Name)这样的参数来使用它,但是它也允许你指定一个条件作为第二个参数,如下所示:

 var myQuery = (from x in _myObject select x).MyDistinct(d => d.Name, x => x.FirstOrDefault(y=>y.Name.Contains("1") || y.Name.Contains("2")) ); 

NB这也可以让你指定其他的function,例如.LastOrDefault(...)


如果您只想公开条件,可以通过将其实现为更简单:

 public static IEnumerable<T> MyDistinct2<T, V>(this IEnumerable<T> query, Func<T, V> f, Func<T,bool> h=null ) { if (h == null) h = (y => true); return query.GroupBy(f).Select(x=>x.FirstOrDefault(h)); } 

在这种情况下,查询将如下所示:

 var myQuery2 = (from x in _myObject select x).MyDistinct2(d => d.Name, y => y.Name.Contains("1") || y.Name.Contains("2") ); 

NB在这里,expression更简单,但注意.MyDistinct2隐式使用.FirstOrDefault(...)


注意:上面的例子使用下面的演示类

 class MyObject { public string Name; public string Code; } private MyObject[] _myObject = { new MyObject() { Name = "Test1", Code = "T"}, new MyObject() { Name = "Test2", Code = "Q"}, new MyObject() { Name = "Test2", Code = "T"}, new MyObject() { Name = "Test5", Code = "Q"} }; 

IEnumerable lambda扩展:

 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()); 

换一种方式:

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

序列返回不同的元素通过属性'_myCaustomerProperty'比较它们。