如何从对象列表中获取不同的列表?
Posted
技术标签:
【中文标题】如何从对象列表中获取不同的列表?【英文标题】:How to get a distinct list from a List of objects? 【发布时间】:2011-06-26 21:03:41 【问题描述】:我有一个List<MyClass> someList
。
class MyClass
public int Prop1...
public int Prop2...
public int Prop3...
我想知道如何从 List<MyClass> someList
中获得一个新的不同的 List<MyClass> distinctList
,但仅将其与 Prop2
进行比较。
【问题讨论】:
重复:***.com/questions/489258/… 【参考方案1】:您可以使用GroupBy
模拟DistinctBy
的效果,然后只使用每个组中的第一个条目。不过可能比其他实现要慢一些。
someList.GroupBy(elem=>elem.Prop2).Select(group=>group.First());
【讨论】:
也适用于多个属性:someList.GroupBy(elem=> new elem.Prop1, elem.Prop2, elem.Prop3 ).Select(group=>group.First());
这应该是被接受的答案。它不依赖任何外部库并且干净
@TejasviHegde:嗯,与使用DistinctBy
相比,它也有缺点:1)它需要更多的内存,因为它为每个元素构建一个组; 2) 它不能流式传输结果——它必须在产生任何元素之前完全读取someList
。我会说 MoreLINQ 方法更干净。
@JonSkeet 谢谢!我没有从这个角度思考:) 顺便说一句。我刚刚浏览了您库的代码,我喜欢这种编码风格和简洁的实现!
这对我帮助很大。我们还可以在末尾添加 .ToList() 。顺便说一句真的很有帮助【参考方案2】:
不幸的是,在框架中并没有真正简单的内置支持 - 但您可以使用我在 MoreLINQ 中的 DistinctBy
实现。
你会使用:
var distinctList = someList.DistinctBy(x => x.Prop2).ToList();
(您可以只采用DistinctBy
实现。如果您更愿意使用Microsoft 实现,我相信Reactive Extensions 的System.Interactive 程序集中也有类似的东西。)
【讨论】:
@Jon,真的值得用 MoreLINQ 来处理这些事情,而不是为IEnumerable.Distinct
实现 IEqualityComparer
吗?
@zerkms:就我个人而言,我会使用 MoreLINQ 或 Reactive Extensions,是的……包含它真的不需要太多努力,而且调用代码最终变得更具可读性 IMO。
@Jon,我只是担心添加对第 3 方代码的依赖。另一方面 - 内置解决方案迫使我们编写臃肿的代码......:-S
@Jon 你碰巧知道他们为什么不向 Distinct 添加一个需要 lambda 的重载吗?包含重载的问题相同。
@Ilya:这很简单:foo.DistinctBy(x => new x.Prop1, x.Prop2 );
【参考方案3】:
您需要使用.Distinct(..);
扩展方法。
这是一个简单的示例:
public class Comparer : IEqualityComparer<Point>
public bool Equals(Point x, Point y)
return x.X == y.X;
public int GetHashCode(Point obj)
return (int)obj.X;
不要忘记GetHashCode
。
用法:
List<Point> p = new List<Point>();
// add items
p.Distinct(new Comparer());
【讨论】:
如果我们在没有new Comparer()
参数的情况下调用p.Distinct();
会发生什么?一般ListOfObjects.Distinct()
是如何工作的?
@ImanMahmoudinasab,一般来说,.NET 将使用对象中的相等方法(Equals 和 GetHashCode)。如果它们没有被定义,它们将通过引用进行比较,因此,一个对象将只等于它自己。
我们如何使用 Distinict 而不像@ImanMahmoudinasab 所说的那样编写“new Comparer()”?
@Parsa 在没有比较器的情况下编写不同的内容很容易p.Distinct();
。仅当您要检查两个 instance 是否相同的引用(相同的内存)不相等(不同的内存但相同的属性值)时,没有比较器的区别才有用。
@Parsa 示例:var a=new Person("Iman"); var b=new Person("Iman"); var pList=new List<Person>(); pList.Add(a); pList.Add(b); pList.Add(a);
在此示例中 pList
包含 a
两次,使用 pList.Distinc()
将只给您 一个 a
和一个 @987654334 @。请注意,a
和 b
具有相同的名称:Iman
。所以pList.Distinc().Count()
是 2。但与比较器 pList.Distinc(new NameComparer()).Count()
是 1.【参考方案4】:
覆盖 Equals(object obj) 和 GetHashCode() 方法:
class MyClass
public int Prop1 get; set;
public int Prop2 get; set;
public int Prop3 get; set;
public override bool Equals(object obj)
return ((MyClass)obj).Prop2 == Prop2;
public override int GetHashCode()
return Prop2.GetHashCode();
然后只需调用:
List<MyClass> distinctList = someList.Distinct().ToList();
【讨论】:
【参考方案5】:自从引入了值元组,如果你想要一个相当于 SQL 的 DISTINCT 的 LINQ
items.GroupBy(item => (item.prop1, item.prop2, ...)).Select(group => group.First())
【讨论】:
【参考方案6】:如果你想通过多个字段来Distinct
你的列表,你必须创建一个IEqualityComparer
接口的实例:
public class MyComparer : IEqualityComparer<MyModel>
public bool Equals(MyModel x, MyModel y)
// compare multiple fields
return
x.Field1 == y.Field1 &&
x.Field2 == y.Field2 &&
x.Field3 == y.Field3 ;
public int GetHashCode(MyModel obj)
return
obj.Field1.GetHashCode() +
obj.Field2.GetHashCode() +
obj.Field3.GetHashCode();
然后使用比较器来区分您的列表:
var distinctedList = myList.Distinct(new MyComparer()).ToList();
【讨论】:
【参考方案7】:创建一个实现IEqualityComparer 接口的类,该接口仅检查您的 Prop2-Property。然后,您可以将此类的实例传递给 Distinct 扩展方法。
【讨论】:
【参考方案8】:我知道已经有一段时间了,但我需要最简单的答案,此时(使用 .NET 4.5.1)我发现以下是我能得到的最直接的答案:
IEnumerable<long> allIds = waitingFiles.Values.Select(wf => wf.groupId).Distinct();
我的情况是我有一个看起来像这样的 ConcurrentDictionary:
ConcurrentDictionary<long, FileModel>
ConcurrentDictionary Values 属性基本上是我的List<FileModel>
。
*FileModel 的 groupId 不一定是唯一的(不过,显然我用来将 FileModel 对象添加到字典中的键(长)对于 FileModel 是唯一的)。
*为清楚起见在示例中命名。
关键是我在 ConcurrentDictionary 中有大量 FileModel(想象一下 100 个),在这 100 个 FileModel 中有 5 个不同的 groupId。
此时我只需要一个不同 groupId 的列表。
所以,如果我只有一个 FileModel 列表,代码将如下所示:
IEnumerable <long> allIds = allFileModel.Select(fm => fm.groupId).Distinct();
【讨论】:
【参考方案9】:删除所有属性相等的重复项的简单方法:
System.Web.Script.Serialization.javascriptSerializer jss = new System.Web.Script.Serialization.JavaScriptSerializer();
serviceList = serviceList.GroupBy(s => jss.Serialize(s)).Select(group => group.First()).ToList();
【讨论】:
以上是关于如何从对象列表中获取不同的列表?的主要内容,如果未能解决你的问题,请参考以下文章
Android:使用 Retrofit 获取不同的对象列表数据