我有一个对象列表
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.
这样做的最佳方式是什么?
答案 0 :(得分:2)
分组时没有理由将列表包装成匿名类型;在调用GroupBy
时,只需突出显示列表。
List
未覆盖Equals
和GetHashCode
,因此它将使用列表的引用而非其内容来定义相等性。您需要创建自定义IEqualityComparer
至compare the values of the sequence。
您正在订购群组,这些群组无法比较,因此您的错误。您想要在每个组中订购项。要执行此操作,请使用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();