与 LINQ 类的属性不同

我有一个收藏:

List<Car> cars = new List<Car>();

汽车通过其属性 CarCode进行唯一标识。

我收藏了三辆车,还有两辆有相同的卡号。

如何使用 LINQ 将此集合转换为具有唯一 CarCode 的 Cars?

199729 次浏览

使用 莫林克,它有一个 DistinctBy方法:)

IEnumerable<Car> distinctCars = cars.DistinctBy(car => car.CarCode);

(请注意,这只是 LINQ to Objects。)

您可以使用分组,并从每组中获得第一辆车:

List<Car> distinct =
cars
.GroupBy(car => car.CarCode)
.Select(g => g.First())
.ToList();

您可以实现一个 IEqualityComparer,并将其用于您的 District 扩展中。

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

完成同样事情的另一种方法..。

List<Car> distinticBy = cars
.Select(car => car.CarCode)
.Distinct()
.Select(code => cars.First(car => car.CarCode == code))
.ToList();

可以创建一个扩展方法来以更通用的方式完成这项工作。如果有人能够针对 GroupBy 方法评估这个“ distinctBy”的性能,那将是非常有趣的。

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

你可以看看我的 强大的扩展库。目前它还处于一个非常年轻的阶段,但是你已经可以使用诸如“区分”、“联合”、“互联”等方法,除了在任意数量的属性上;

这就是你如何使用它:

using PowerfulExtensions.Linq;
...
var distinct = myArray.Distinct(x => x.A, x => x.B);

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&lt;TSource&gt;.</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;
}
}

我认为在性能方面(或任何方面)最好的选择是使用 平等比较器接口来区分。

尽管每次为每个类实现一个新的比较器是很麻烦的,并且会产生样板代码。

因此,这里有一个扩展方法,它可以动态地为使用反射的任何类生成一个新的 平等比较器

用法:

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方法,它将检查对象的引用,它们并不是不同的。