独特()与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'比较它们。