从列表中删除具有最低Num属性的重复对象

时间:2015-01-06 18:14:45

标签: c# linq

我有一个对象列表

public class Obj
{
    public int Num {get;set;}
    public List<string> StrList {get;set;}
    ...other irrelevant properties
}

Obj列表中的某些项目将包含相同的StrList,但会有不同的Num。我想删除列表中具有相似Obj的所有StrList,并保留具有最大Num的那个。

这是我的尝试:

objList = objList.GroupBy(x => new { x.StrList }).OrderByDescending(x => x.Select(y => y.Num)).Select(x => x.First()).ToList();

但我收到错误:At least one object must implement IComparable.

这样做的最佳方式是什么?

2 个答案:

答案 0 :(得分:2)

  1. 分组时没有理由将列表包装成匿名类型;在调用GroupBy时,只需突出显示列表。

  2. List未覆盖EqualsGetHashCode,因此它将使用列表的引用而非其内容来定义相等性。您需要创建自定义IEqualityComparercompare the values of the sequence

  3. 您正在订购群组,这些群组无法比较,因此您的错误。您想要在每个组中订购。要执行此操作,请使用Select,然后对要投影的组进行排序(并获取生成的查询中的第一个),而不是对外部集合进行排序。

答案 1 :(得分:1)

您不能按列表进行分组,即使是有序的列表,也可以按照特定顺序连接字符串生成的单个字符串进行分组。假设有一个字符永远不会出现在任何字符串中(比如'|'),你可以这样做:

objList = objList
    .GroupBy(x => string.Join("|", x.StrList.OrderBy(s => s)))
    .Select(g => g.OrderByDescending(x => x.Num).First())
    .ToList();

这个想法是通过以相同的顺序连接它们来构建一个分组键(升序词典)。

这适用于少量数据。如果您的列表非常大,您可以实现一个特殊的类来用作分组的多部分键,而不会产生可能很大的键:

public class MultipartKey : IEquitable<MultipartKey> {
    private IList<string> parts;
    int hashCode;
    public MultipartKey(IEnumerable<string> parts) {
        this.parts = parts.OrderBy(s => s).ToList();
        hashCode = this.parts.Aggregate(0, (p, v) => 31*p + v.GetHashCode());
    }
    public override int GetHashCode() {
        return hashCode;
    }
    public override bool Equals(MultipartKey other) {
        return parts.SequenceEqual(other.Parts);
    }
}

并在像这样的查询中使用它:

objList = objList
    .GroupBy(x => new MultipartKey(x.StrList))
    .Select(g => g.OrderByDescending(x => x.Num).First())
    .ToList();