使用LINQ获取自定义对象列表的总和/平均值

8

我有一个类:

public class PointD
{
    public double X
    { get; set; }
    public double Y
    { get; set; }

    public PointD(double x, double y)
    {
        X = x;
        Y=y;
    }
   //operator for +,-, * and / are overridden
 }

如何使用LINQ获取list<PointD>的平均值?等价于使用for循环的代码如下:

  double xx, yy;
  for ( int i=0; i< ptlist.Count; i++)
     {
         xx+=ptlist[i].X;
         yy+=ptlist[i].Y; 
     }
  return new PointD(){X=xx, Y=yy};

您只能使用任何内置的LINQ函数。 您不能定义一个扩展来处理此函数。

有什么想法吗?

编辑:当然,您可以在合并它们之前使用两个单独的Sum扩展方法对X和Y分量进行求和。 但这不是我想要的。 我想要的是一个单一的查询/方法来完成工作


两个求和不可以吗?像这样:ptlist.Sum(p => p.X); 和 ptlist.Sum(p => p.Y); - albertjan
如何使用LINQ获取它的平均值?我想你是指总和。 - James
@the_ajp:它可以工作,但我想把所有东西都放在一个查询中,这就是问题所在。 - Graviton
3个回答

13

这里可以使用Aggregate函数。

var sum = list.Aggregate((acc, cur) => acc + cur);
var average = list.Aggregate((acc, cur) => acc + cur) / list.Count;

只要确保你为类型PointDint定义了/运算符,这样就可以完成任务。

注意:我不确定你是想要求和还是平均值(你的问题有些模糊),但我已经为两者都提供了示例。


PointD是一个引用类型,因此根据操作符的实现方式,您可能会在这里得到ArgumentNullException或NullReferenceException。可能值得为累加器提供一个非空种子。 - Greg Beech
这是一个好的想法,但我不太明白为什么我没能想到它.. 哎呀!! - Graviton
@Greg Beech:这是一个公正的观点。我自动假设它是一个struct(就像WPF/System.Drawing中的Point类型),但似乎他将其定义为一个类。 @Ngu Soon Hui:我建议您将PointD类型定义为struct,除非您有充分的理由不这样做。然后,Aggregate将使用default(PointD)作为种子,这意味着一切都会正常工作。 - Noldorin

6

您需要使用Aggregate方法,以便您可以提供自己的聚合函数(Sum只是这种方法的一个方便的特殊情况)。类似这样:

points.Aggregate(new PointD(0, 0), (a, p) => a + p);

我知道你说你不想定义任何额外的方法,但如果这是一个常见的操作,我倾向于为此编写一个扩展方法,即:

public static PointD Sum(this IEnumerable<PointD> source)
{
    return source.Aggregate(new PointD(0, 0), (a, p) => a + p);
} 

因为写起来更易读,能够这样写:

points.Sum();

1
如果他已经为PointD重载了运算符,就没有必要重新定义加法。 - Noldorin
你能解释一下 new PointD(0, 0) 的目的吗? - jxramos

0
    [Fact]
    public void CanAddManyPointDs()
    {
        var points = new[]{
            new PointD( 1,1),
            new PointD( 2,3),
            new PointD( 3,4),
        };

        var result = points.Aggregate((p1, p2) => new PointD(p1.X + p2.X, p1.Y + p2.Y));
        Assert.Equal(result.X,6);
        Assert.Equal(result.Y,8);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接