不同于linq的类的属性
我有一个集合:
List<Car> cars = new List<Car>();
汽车是由他们的财产CarCode
唯一标识。
我有三辆车在收集,和两个相同的CarCodes。
我如何使用LINQ将这个集合转换成独特的CarCodes的汽车?
您可以使用分组,并从每组获得第一辆车:
List<Car> distinct = cars .GroupBy(car => car.CarCode) .Select(g => g.First()) .ToList();
使用MoreLINQ ,其中有一个DistinctBy
方法:)
IEnumerable<Car> distinctCars = cars.DistinctBy(car => car.CarCode);
(这只适用于LINQ to Objects,请注意。)
与Guffa一样的方法,但作为扩展方法:
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property) { return items.GroupBy(property).Select(x => x.First()); }
用作:
var uniqueCars = cars.DistinctBy(x => x.CarCode);
您可以实现一个IEqualityComparer,并将其用于Distinct扩展。
class CarEqualityComparer : IEqualityComparer<Car> { #region IEqualityComparer<Car> Members public bool Equals(Car x, Car y) { return x.CarCode.Equals(y.CarCode); } public int GetHashCode(Car obj) { return obj.CarCode.GetHashCode(); } #endregion }
接着
var uniqueCars = cars.Distinct(new CarEqualityComparer());
我认为在性能方面(或任何方面)的最佳select是使用IEqualityComparer接口区分。
虽然每次为每个类实现一个新的比较器都很麻烦,并产生样板代码。
所以这里是一个扩展方法,它使用reflection为任何类生成一个新的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(); } }
Linq-to-Objects的另一种扩展方法,不使用GroupBy:
/// <summary> /// Returns the set of items, made distinct by the selected value. /// </summary> /// <typeparam name="TSource">The type of the source.</typeparam> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="source">The source collection.</param> /// <param name="selector">A function that selects a value to determine unique results.</param> /// <returns>IEnumerable<TSource>.</returns> public static IEnumerable<TSource> Distinct<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) { HashSet<TResult> set = new HashSet<TResult>(); foreach(var item in source) { var selectedValue = selector(item); if (set.Add(selectedValue)) yield return item; } }
另一种方法来完成相同的事情…
List<Car> distinticBy = cars .Select(car => car.CarCode) .Distinct() .Select(code => cars.First(car => car.CarCode == code)) .ToList();
可以创build一个扩展方法以更通用的方式来完成此操作。 如果有人能够评估这个“DistinctBy”对GroupBy方法的performance,这将是有趣的。
你可以看看我的PowerfulExtensions库。 目前处于一个非常年轻的阶段,但已经可以使用像Distinct,Union,Intersect等方法,除了任何数量的属性,
这是你如何使用它:
using PowerfulExtensions.Linq; ... var distinct = myArray.Distinct(x => xA, x => xB);
你不能有效地使用Distinct
对象的集合(没有额外的工作)。 我会解释为什么。
该文件说 :
它使用默认的相等比较器
Default
来比较值。
对于这意味着它使用默认方程方法来比较对象( 源 )。 这是他们的哈希码。 而且由于你的对象没有实现GetHashCode()
和Equals
方法,所以它会检查对象的引用,这些引用是不清楚的。