1

I have a Listof points and I want to calculate the remaining distance to the end using Linq (given an index):

double remainingToEnd = Points.Skip(CurrentIndex).Aggregate((x, y) => x.DistanceTo(y));

This doesn't compile:

Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type

I normally solve this situation projecting by using the Select extension, but that would prevent me from calculating the distance afterwards.

This is easily achieved by using a loop but I want to know if it is possible with some simple Linq. I would like to avoid anonymous types too.


Point is defined like:

public class Point
{
    public float X { get; set; }
    public float Y { get; set; }
    public float Z { get; set; }

    public float DistanceTo(Point p2)
    {
        float x = this.X - p2.X;
        float y = this.Y - p2.Y;
        float z = this.Z - p2.Z;

        return (float)Math.Sqrt((x * x) + (y * y) + (z * z));
    }
}
Sturm
  • 3,968
  • 10
  • 48
  • 78
  • 1
    Hi, what's the return type of `DistanceTo`? – Christos Mar 17 '17 at 21:45
  • Sorry, it is a double – Sturm Mar 17 '17 at 21:48
  • Not a problem at all :) Just wanted some more info. Thanks – Christos Mar 17 '17 at 21:48
  • There is an overload that takes an initial value of the result type. However, your usage is incorrect. The function you provide must take the running total and the next element, not two elements. I think what you're really looking for is an aggregation of adjacent pairs ([0,1] + [1,2] + [2,3] + ...). There are definitely questions on how to obtain adjacent pairs, including [this one](http://stackoverflow.com/questions/1624341/getting-pair-set-using-linq). – chris Mar 17 '17 at 21:50
  • Could you also please post the definition of `Points`. I suppose it is a `List`. Please include also the definition of `Point`. Thanks in advance. – Christos Mar 17 '17 at 21:51
  • @Sturm can you provider full signature of `DistanceTo` method and type of items in `Points` collection? – Sergey Berezovskiy Mar 17 '17 at 21:52
  • Thanks @chris it seems that it indeed requires anonymous type to store values. – Sturm Mar 17 '17 at 22:01
  • @Sturm, Huh? What does? – chris Mar 17 '17 at 22:03

2 Answers2

1

Assume you want to calculate total distance between points in collection (starting from some index). You need previous point on each step. You can get it by zipping points collection with itself:

double remainingToEnd = Points.Skip(CurrentIndex)
                              .Zip(Points.Skip(CurrentIndex + 1), (x,y) => x.DistanceTo(y))
                              .Sum();

Zip will produce pairs of starting and ending points. Result selector function will select distance between points for each pair. And then you just calculate sum or distances.


You can solve this task with aggregation as will, but you need to store last point on each step. So you need accumulator which will keep both current distance and last point:

var remainingToEnd = Points.Skip(CurrentIndex).Aggregate(
            new { total = 0.0, x = Points.Skip(CurrentIndex).FirstOrDefault() },
            (a, y) => new { total = a.total + a.x.DistanceTo(y), x = y },
            a => a.total);

And keep in mind, that Skip means just iterating your sequence item by item without doing anything. If you have a lot of points, skipping twice can hurt your performance. So if you have list of points, and performance matters, then simple for loop will do the job:

double remainingToEnd = 0.0;
for(int i = CurrentIndex; i < Points.Count - 1; i++)
   remainingToEnd += Points[i].DistanceTo(Points[i+1]);
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • Thanks, what do you think about performance when compared to a simple loop: `for(int i = CurrentIndex; i < Points.Count; i++) { /* total += p1.DistanceTo(p2) */ }` – Sturm Mar 17 '17 at 22:01
  • @Sturm performance of simple loop always be better than LINQ performance. Internally it uses loop too, but also you have some objects creation and additional method calls in background. LINQ gives you compact and expressive code. And don't think that there will be huge performance differrence unless you have high load system which constantly runs this piece of code – Sergey Berezovskiy Mar 17 '17 at 22:04
0

Try this:

double remainingToEnd =   Points.Skip(CurrentIndex).Sum(point =>  point.DistanceTo(Points[Points.Findindex(p => p == point) - 1]));
Abdullah Dibas
  • 1,499
  • 1
  • 9
  • 13