tags:

views:

459

answers:

4

I have a class:

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
 }

Given a list<PointD>, how to get the average of it using LINQ? A for loop equivalent will be something like this:

  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};

You can use any built-in LINQ function only. You can't define an extension that takes care of this function.

Any idea?

Edit: Of course, you can use two separate Sum extension method to Sum for X and Y component before merging them. But this is not what I want. What I want is a single query/ method that does the job

+2  A: 

You'll need to use the Aggregate method instead so you can provide your own aggregation function (Sum is just a convenient specialized case of this method). Something like:

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

I know you say you don't want any additional methods defined, but if this is a common operation I'd be inclined to write an extension method for this, i.e.

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

Because it's much more readable to be able to write:

points.Sum();
Greg Beech
If he's already overloaded the operators for `PointD`, there's no need to redefine addition.
Noldorin
Oh yeah. Didn't see that in the question. OK, I'll edit.
Greg Beech
A: 
public GetSumPointD(List<PointD> points)
{
     return new PointD(points.Sum(points => points.X), points.Sum(points => points.Y));
}
James
+2  A: 

The Aggregate function would come in handy here.

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

Just insure that you have the / operator defined for types PointD and int, and this should do the job.

Note: I'm not quite sure whether you want the sum or average here (your question is somewhat ambiguous about this), but I've included examples for both.

Noldorin
PointD is a reference type, so depending on how the operators are implemented you may very well get an ArgumentNullException or NullReferenceException here. Possibly worth providing a non-null seed to the accumulator.
Greg Beech
This is a good one, but I don't quite understand why I couldn't think of it.. duh!!
Ngu Soon Hui
@Greg Beech: That's a fair point. I automatically assumed it was a `struct` (as the `Point` type in WPF/System.Drawing) is, but it seems he defined it as a class.@Ngu Soon Hui: I recommend you define your `PointD` type as a `struct`, unless you have good reason otherwise. `Aggregate` will then use `default(PointD)` as the seed, which means things will work fine.
Noldorin
A: 
    [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);
    }
Ruben Bartelink