与 LINQ 的类的属性不同 [重复]

Posted

技术标签:

【中文标题】与 LINQ 的类的属性不同 [重复]【英文标题】:Distinct by property of class with LINQ [duplicate] 【发布时间】:2011-02-02 00:41:45 【问题描述】:

我有一个收藏:

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

汽车由其属性 CarCode 唯一标识。

我的收藏中有三辆汽车,其中两辆具有相同的 CarCode。

如何使用 LINQ 将此集合转换为具有唯一 CarCodes 的汽车?

【问题讨论】:

相关/可能重复:LINQ's Distinct() on a particular property 【参考方案1】:

您无法在对象集合上有效地使用Distinct(无需额外工作)。我会解释原因。

The documentation says:

它使用默认的相等比较器 Default 来比较值。

对于对象,这意味着它使用默认方程式方法来比较对象 (source)。那是在他们的哈希码上。并且由于您的对象没有实现 GetHashCode()Equals 方法,它会检查对象的引用,它们并不不同。

【讨论】:

【参考方案2】:

使用MoreLINQ,它有一个DistinctBy 方法:)

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

(请注意,这仅适用于 LINQ to Objects。)

【讨论】:

只是提供链接!code.google.com/p/morelinq/source/browse/MoreLinq/… 嗨,乔恩,如果可以的话,有两个问题。 1)为什么不将库添加到 Nuget? 2) LINQ to SQL\EF\NH 怎么样?我们如何实施呢?我们是否必须使用 Guffa 版本(如果NO_HASHSET 是真的,那是你的版本......)?非常感谢! @gdoron: 1) 它已经在 NuGet 中了:nuget.org/packages/morelinq 2) 我怀疑 LINQ to SQL 等是否足够灵活以允许这样做。 哦,这是预发行版...这就是我找不到它的原因。 2)好吧,我担心将Lib添加到我的项目中,我担心有人会将它与IQueryable&lt;T&gt;一起使用并尝试DistinctBy它并因此查询整个该死的表......这不是很容易出错?再次感谢您的快速回复! @Shimmy:我个人对在System 下编写代码感到紧张,因为这给人一种“官方”的错误印象。但是你的口味可能会有所不同,当然:)【参考方案3】:

我认为在性能方面(或任何方面)的最佳选择是使用 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();
    

【讨论】:

这里的反射在哪里?【参考方案4】:

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;
        
    

【讨论】:

【参考方案5】:

您可以查看我的 PowerfulExtensions 库。目前它还处于非常年轻的阶段,但您已经可以在任意数量的属性上使用 Distinct、Union、Intersect、Except 等方法;

这是你使用它的方式:

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

【讨论】:

如果我有一个对象列表,我想删除所有具有相同 ID 的对象,它会是 myList.Distinct(x =&gt; x.ID) 吗?【参考方案6】:

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

【讨论】:

完美。 Microsoft.Ajax.Utilities 库也提供了同样的方法。【参考方案7】:

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

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

可以创建一个扩展方法以更通用的方式执行此操作。如果有人可以针对 GroupBy 方法评估这种“DistinctBy”的性能,那将会很有趣。

【讨论】:

第二个 Select 将是 O(n*m) 操作,因此无法很好地扩展。如果有很多重复项,即如果第一个 Select 的结果是原始集合的一小部分,它可能会表现得更好。【参考方案8】:

您可以实现一个 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());

【讨论】:

我们如何在不写的情况下使用它:new CarEqualityComparer()? @Parsa 您可以创建一个接受 lambdas 的 IEqualitiyComparer 包装器类型。这将使它泛化:cars.Distinct(new GenericEqualityComparer&lt;Car&gt;((a,b) =&gt; a.CarCode == b.CarCode, x =&gt; x.CarCode.GetHashCode()))。我过去使用过这种方法,因为它有时会在执行一次性 Distinct 时增加价值。【参考方案9】:

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

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

【讨论】:

@NateGates:我正在和两天前投反对票的人交谈。 我认为不存在开销! @AmirHosseinMehrvarzi:有一点开销,因为创建了组,然后每个组只使用一个项目。 更多键写:.GroupBy(car =>new car​​.CarCode,car.PID,car.CID) @Nani 一般来说你是对的,但是由于只有在集合中有匹配的元素时才会创建一个组,所以每个组至少有一个元素。 --> First() 在这个用例中完全没问题。

以上是关于与 LINQ 的类的属性不同 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

C#替代静态类的继承[重复]

在具有 C++ 中另一个类型的类中声明属性 [重复]

Linq distinct 方法仅适用于特定属性[重复]

Objective-C - 从不同的类访问属性[重复]

错误:“指定的 LINQ 表达式包含对与不同上下文关联的查询的引用”[重复]

LINQ按名称选择属性[重复]