【问题标题】:C# LINQ Distinct(f=>f.propertyname) doesnt work [duplicate]C# LINQ Distinct(f=>f.propertyname) 不起作用 [重复]
【发布时间】:2014-03-13 08:34:21
【问题描述】:

我正在尝试对复杂类型的集合编写 LINQ 查询。我想针对两个字段的组合在此集合上编写一个不同的查询。

我认为 Lambda 表达式不支持 Distinct(f=>f.propertyname) 的管道。希望它做到了。有人使用了比使用比较器更简单的实现吗?

【问题讨论】:

标签: c# linq distinct


【解决方案1】:

你可以使用MoreLINQ包中的DistinctBy方法。

var result = items.DistinctBy(f => f.PropertyName);

您可以使用匿名类型 DistinctBy 以获得两列不同的结果。

var result = items.DistinctBy(f => new { f.Property1, f.Property2});

【讨论】:

  • DISTINCTBY 不是标准的 LINQ 功能
  • @AmitAgrawal 谁说它应该是标准功能?
  • @AmitAgrawal 我已经说过它在 MoreLINQ 包中。
  • @AmitAgrawal 好吧,他并没有说他不能安装 NuGet 包。这比编写自定义相等比较器要容易。
  • @AmitAgrawal 让我们建议选项,它的操作员是否要求使用它。谁知道它将来可能对其他人有用。
【解决方案2】:

使用纯 LINQ,您可以按所需的所有属性进行分组,然后从每个组中选择第一项:

var result = items.GroupBy(i => new { i.Prop1, i.Prop2 })
                  .Select(g => g.First());

优点:

  • 您无需创建自定义比较器、修改您的类或引用第三方库。
  • 如果您从数据库中查询项目,它可以翻译成 SQL 并在服务器端执行

缺点:

  • 使用 Linq to Objects MoreLINQ 方法更有效(尽管您不应该进行过早的优化)
  • MoreLINQ 语法更具表现力

【讨论】:

    【解决方案3】:

    你不能像这样使用 distinct。编译器无法知道要采用多个值中的哪一个。您可以使用 GroupBy() 组合它们,也可以根据需要使用 Min()、Max()、First()、Last() 或类似方法从列表中选择一个。

    【讨论】:

      【解决方案4】:

      您有多种选择:

      1. 您可以为 Distinct 的重载实现自定义 IEqualityComparer<T>
      2. 或在您的班级中覆盖Equals + GethashCode
      3. 另一种(效率较低)不需要创建新类或修改现有类的方法是使用匿名类型的内置GetHashCode+EqualsEnumerable.GroupBy

        IEnumerable<Complex> distinctObjects = 
            from c in collection
            group c by  new { c.Prop1, c.Prop2 } into g 
            select g.First(); // change logic if you don't want an arbitrary object(first)
        

      这是第二种方法的示例:

      public class Complex
      {
          public string Prop1 { get; set; }
          public int Prop2 { get; set; }
          public Complex Prop3 { get; set; }
      
          public override bool Equals(object obj)
          {
              Complex c2 = obj as Complex;
              if (obj == null) return false;
              return Prop1 == c2.Prop1 && Prop2 == c2.Prop2;
          }
      
          public override int GetHashCode()
          {
              unchecked // Overflow is fine, just wrap
              {
                  int hash = 17;
                  hash = hash * 23 + Prop1.GetHashCode();
                  hash = hash * 23 + Prop2;
                  return hash;
              }
          }
      }
      

      IEqualityComparer 类(1. 方法)的 EqualsGethashCode 将是相似的。

      【讨论】:

        【解决方案5】:

        您可以使用您的两个属性动态创建一个匿名类型,然后使用 distinct。

        像这样:

        [Test]
        public void Distinct_with_anonymous_type()
        {
            var items = new[]
            {
                new {p1 = 'a', p2 = 2, p3=10},   // double over p1 and p2
                new {p1 = 'a', p2 = 3, p3=11}, 
                new {p1 = 'a', p2 = 2, p3=12},   // double over p1 and p2
            };
        
            var expected = new[]
            {
                new {p1 = 'a', p2 = 2},
                new {p1 = 'a', p2 = 3}, 
            };
        
            var distinct = items.Select(itm => new { itm.p1, itm.p2 }).Distinct();
        
            Assert.That(distinct, Is.EquivalentTo(expected));
        }
        

        希望这会有所帮助!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多