Отличается свойством класса с LINQ
у меня есть коллекция:
List<Car> cars = new List<Car>();
автомобили однозначно идентифицируются по их свойству CarCode.
у меня есть три автомобиля в коллекции, и два с одинаковыми каркасами.
Как я могу использовать LINQ для преобразования этой коллекции в автомобили с уникальными Каркодами?
9 ответов:
вы можете использовать группировку и получить первый автомобиль из каждой группы:
List<Car> distinct = cars .GroupBy(car => car.CarCode) .Select(g => g.First()) .ToList();
использовать MoreLINQ, которая имеет
DistinctByспособ :)IEnumerable<Car> distinctCars = cars.DistinctBy(car => car.CarCode);(это только для LINQ объектов, заметьте.)
тот же подход, что и 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 и использовать его в своем отдельном расширении.
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());
другой метод расширения для 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; } }
Я думаю, что лучший вариант с точки зрения производительности (или в любых терминах) заключается в различном использованииIEqualityComparer интерфейс.
хотя реализация каждый раз новый компаратор для каждого класса является громоздким и производит шаблонного кода.
Итак, вот метод расширения, который создает новый 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(); } }
вы не можете эффективно использовать
Distinctна коллекции объектов (без дополнительной работы). Я объясню, почему.используется компаратор проверки на равенство по умолчанию,
Default, чтобы сравнить значения.для объектов, это означает, что он использует метод уравнения по умолчанию для сравнения объектов (источник). То есть на их хэш-код. И поскольку ваши объекты не реализуют
GetHashCode()иEqualsметоды, он будет проверять на ссылку объекта, которые не различны.
другой способ сделать то же самое...
List<Car> distinticBy = cars .Select(car => car.CarCode) .Distinct() .Select(code => cars.First(car => car.CarCode == code)) .ToList();можно создать метод расширения, чтобы сделать это в более общем виде. Было бы интересно, если бы кто-то мог оценить эффективность этого "DistinctBy" против подхода GroupBy.
вы можете проверить мои мощные расширения библиотека. В настоящее время он находится на очень молодой стадии, но уже можно использовать такие методы, как Distinct, Union, Intersect, за исключением любого количества свойств;
вот как вы его используете:
using PowerfulExtensions.Linq; ... var distinct = myArray.Distinct(x => x.A, x => x.B);
Comments