使用LINQ计算与上一项的差异

Mar*_*rty 19 linq diff pivot

我正在尝试使用LINQ为图形准备数据.

我无法解决的问题是如何计算"与以前的差异.

我期望的结果是

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

ID = 1,日期=现在+ 1,DiffToPrev = 3;

ID = 1,日期=现在+ 2,DiffToPrev = 7;

ID = 1,日期=现在+ 3,DiffToPrev = -6;

等等...

你能帮我创建这样的查询吗?

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(4),Value = 15},
            new MyObject {ID= 2,Date = DateTime.Now.AddDays(5),Value = 18}

        };

            Console.WriteLine(list);   

            Console.ReadLine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 59

一个选项(对于LINQ to Objects)将创建自己的LINQ运算符:

// 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这使您可以仅使用源序列的一次传递来执行投影,这总是一个奖励(想象一下在大型日志文件上运行它).

请注意,它会将一系列长度投影n到一个长度序列中n-1- 例如,您可能希望在前面添加一个"虚拟"第一个元素.(或者更改方法以包含一个.)

以下是您如何使用它的示例:

var query = list.SelectWithPrevious((prev, cur) =>
     new { ID = cur.ID, Date = cur.Date, DateDiff = (cur.Date - prev.Date).Days) });
Run Code Online (Sandbox Code Playgroud)

请注意,这将包括一个ID的最终结果以及下一个ID的第一个结果...您可能希望先按ID对序列进行分组.

  • 这是一个很好的小功能Jon; 甜美而简单. (2认同)

Bra*_*mir 17

使用index获取上一个对象:

   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)
          }
   );
Run Code Online (Sandbox Code Playgroud)

  • @JonSkeet OP有一个列表,并没有要求一般用途,所以这是一个优越的答案. (2认同)
  • @JimBalter:Stack Overflow 的目的不仅仅是服务于 OP 的问题。有时严格遵守所需内容的界限是有意义的(尽管我至少已格式化此代码以避免滚动),但有时我认为提供更通用的方法会有所帮助。 (2认同)

Fel*_*man 6

在C#4中,您可以使用Zip方法一次处理两个项目.像这样:

        var list1 = list.Take(list.Count() - 1);
        var list2 = list.Skip(1);
        var diff = list1.Zip(list2, (item1, item2) => ...);
Run Code Online (Sandbox Code Playgroud)


Edy*_*dyn 5

修改Jon Skeet的答案以不跳过第一项:

public static IEnumerable<TResult> SelectWithPrev<TSource, TResult>
    (this IEnumerable<TSource> source, 
    Func<TSource, TSource, bool, TResult> projection)
{
    using (var iterator = source.GetEnumerator())
    {
        var isfirst = true;
        var previous = default(TSource);
        while (iterator.MoveNext())
        {
            yield return projection(iterator.Current, previous, isfirst);
            isfirst = false;
            previous = iterator.Current;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

几个主要区别...传递第三个bool参数,以指示它是否是可枚举的第一个元素。我还切换了当前/先前参数的顺序。

这是匹配的示例:

var query = list.SelectWithPrevious((cur, prev, isfirst) =>
    new { 
        ID = cur.ID, 
        Date = cur.Date, 
        DateDiff = (isfirst ? cur.Date : cur.Date - prev.Date).Days);
    });
Run Code Online (Sandbox Code Playgroud)