【问题标题】: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 进行比较。

【问题讨论】:

标签: c# linq class list properties


【解决方案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();
            

            【讨论】:

              猜你喜欢
              • 2015-03-07
              • 2020-11-23
              • 1970-01-01
              • 2019-07-03
              • 2020-09-11
              • 1970-01-01
              • 1970-01-01
              • 2020-02-15
              • 1970-01-01
              相关资源
              最近更新 更多