如何从对象列表中获取不同的列表?

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&lt;Person&gt;(); pList.Add(a); pList.Add(b); pList.Add(a); 在此示例中 pList 包含 a 两次,使用 pList.Distinc() 将只给您 一个 a 和一个 @987654334 @。请注意,ab 具有相同的名称: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&lt;long, FileModel&gt;

ConcurrentDictionary Values 属性基本上是我的List&lt;FileModel&gt;

*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 获取不同的对象列表数据

根据具有不同对象的匹配字段从数组列表中删除重复元素

从具有不同值的逗号分隔 JSON 对象填充 html 下拉列表

如何以交替顺序对两个不同对象列表的 XML 数组项进行排序?