tags:

views:

126

answers:

3

I'm trying to prepare data for a graph using LINQ.

The problem that i cant solve is how to calculate the "difference to previous.

the result I expect is

ID= 1, Date= Now, DiffToPrev= 0;

ID= 1, Date= Now+1, DiffToPrev= 3;

ID= 1, Date= Now+2, DiffToPrev= 7;

ID= 1, Date= Now+3, DiffToPrev= -6;

etc...

Can You help me create such a query ?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    public class MyObject
    {
        public int ID { get; set; }
        public DateTime Date { get; set; }
        public int Value { get; set; }
    }

    class Program
    {
        static void Main()
        {
              var list = new List<MyObject>
              {
                new MyObject {ID= 1,Date = DateTime.Now,Value = 5},
                new MyObject {ID= 1,Date = DateTime.Now.AddDays(1),Value = 8},
                new MyObject {ID= 1,Date = DateTime.Now.AddDays(2),Value = 15},
                new MyObject {ID= 1,Date = DateTime.Now.AddDays(3),Value = 9},
                new MyObject {ID= 1,Date = DateTime.Now.AddDays(4),Value = 12},
                new MyObject {ID= 1,Date = DateTime.Now.AddDays(5),Value = 25},
                new MyObject {ID= 2,Date = DateTime.Now,Value = 10},
                new MyObject {ID= 2,Date = DateTime.Now.AddDays(1),Value = 7},
                new MyObject {ID= 2,Date = DateTime.Now.AddDays(2),Value = 19},
                new MyObject {ID= 2,Date = DateTime.Now.AddDays(3),Value = 12},
                new MyObject {ID= 2,Date = DateTime.Now.AddDays(3),Value = 15},
                new MyObject {ID= 2,Date = DateTime.Now.AddDays(4),Value = 18},
                new MyObject {ID= 2,Date = DateTime.Now.AddDays(5),Value = 19}
            };

            Console.WriteLine(list);   

            Console.ReadLine();
        }
    }
}
+2  A: 

In C#4 you can use the Zip method in order to process two items at a time. Like this:

        var list1 = list.Take(list.Count() - 1);
        var list2 = list.Skip(1);
        var diff = list1.Zip(list2, (item1, item2) => ...);
Felix Ungman
+2  A: 

One option (for LINQ to Objects) would be to create your own LINQ operator:

// I don't like this name :(
public static IEnumerable<TResult> SelectWithPrevious<TSource, TResult>
    (this IEnumerable<TSource> source,
     Func<TSource, TSource, TResult> projection)
{
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
             yield break;
        }
        TSource previous = iterator.Current;
        while (iterator.MoveNext())
        {
            yield return projection(previous, iterator.Current);
            previous = iterator.Current;
        }
    }
}

This enables you to perform your projection using only a single pass of the source sequence, which is always a bonus (imagine running it over a large log file).

Note that it will project a sequence of length n into a sequence of length n-1 - you may want to prepend a "dummy" first element, for example. (Or change the method to include one.)

Here's an example of how you'd use it:

var query = list.SelectWithPrevious((prev, cur) =>
     new { ID = cur.ID, Date = cur.Date, DateDiff = (cur.Date - prev.Date).Days);

Note that this will include the final result of one ID with the first result of the next ID... you may wish to group your sequence by ID first.

Jon Skeet
This seems like a right answer, but i cant figure how to use it.
Martynas
I guess this one would be more efficient than Branimir's answer, right ?
Martynas
@Martynas: It's more general than Branimir's answer, and more efficient than Felix's.
Jon Skeet
Cool :) seems i need to deepen my Linq knowledge more that i thought. Thank You.
Martynas
+2  A: 

Use index to get previous object:

var LinqList = list.Select((myObject, index) => new {ID = myObject.ID, Date = myObject.Date, Value = myObject.Value, DiffToPrev = (index > 0 ? myObject.Value - list[index - 1].Value : 0)});
Branimir
Thanx :) That simple :) cool :)
Martynas
@Martynas: Note that this isn't very general purpose though - it only works in scenarios where you can index into the collection.
Jon Skeet
@Martynas Thanks@Jon Skeet You are right, but it is simple
Branimir