使用 LINQ 计算与前一项的差异
我正在尝试使用 LINQ 为图表准备数据。
我无法解决的问题是如何计算“与之前的差异”。
我期望的结果是
ID= 1, Date= Now, DiffToPrev= 0;
ID= 1, Date= Now+1, DiffToPrev= 3;
ID= 1 , Date= Now+2, DiffToPrev=
ID= 1, Date= Now+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();
}
}
}
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(4),Value = 15},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(5),Value = 18}
};
Console.WriteLine(list);
Console.ReadLine();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
一个选项(对于 LINQ to Objects)是创建您自己的 LINQ 运算符:
这使您能够仅使用源序列的单遍来执行投影,这始终是一个好处(想象一下在大型日志文件上运行它)。
请注意,它将把长度为
n
的序列投影为长度为n-1
的序列 - 例如,您可能需要在前面添加一个“虚拟”第一个元素。 (或者更改方法以包含一个。)以下是如何使用它的示例:
请注意,这将包括一个 ID 的最终结果和下一个 ID 的第一个结果...您可能希望对序列进行分组先通过ID。
One option (for LINQ to Objects) would be to create your own LINQ operator:
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 lengthn-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:
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.
使用索引获取前一个对象:
Use index to get previous object:
在 C#4 中,您可以使用 Zip 方法一次处理两个项目。像这样:
In C#4 you can use the Zip method in order to process two items at a time. Like this:
修改 Jon Skeet 的答案以不跳过第一项:
一些关键差异...传递第三个 bool 参数来指示它是否是可枚举的第一个元素。我还切换了当前/先前参数的顺序。
这是匹配示例:
Modification of Jon Skeet's answer to not skip the first item:
A few key differences... passes a third bool parameter to indicate if it is the first element of the enumerable. I also switched the order of the current/previous parameters.
Here's the matching example:
除了上面 Felix Ungman 的文章之外,下面是如何使用 Zip() 获取所需数据的示例:
基本上,您正在压缩同一列表的两个版本,但第一个版本(当前列表)从集合中的第二个元素,否则差异将始终与同一元素不同,从而差异为零。
我希望这是有道理的,
戴夫
Further to Felix Ungman's post above, below is an example of how you can achieve the data you need making use of Zip():
Basically, you are zipping two versions of the same list but the first version (the current list) begins at the 2nd element in the collection, otherwise a difference would always differ the same element, giving a difference of zero.
I hope this makes sense,
Dave
Jon Skeet 版本的另一个模组(感谢您的解决方案+1)。只不过这是返回一个可枚举的元组。
这不返回第一个,因为它是返回项目之间的中间值。
使用方式如下:
OR
OR (如乔恩表演)
Yet another mod on Jon Skeet's version (thanks for your solution +1). Except this is returning an enumerable of tuples.
This is NOT returning the first because it's about returning the intermediate between items.
use it like:
OR
OR (like jon shows)
以下是使用
readonly struct
和ValueTuple
(也是struct
)使用 C# 7.2 重构的代码。我使用
Zip()
创建 5 个成员的(CurrentID, PreviousID, CurrDate, PrevDate, DiffToPrev)
元组。使用foreach
可以轻松迭代:完整代码:
单元测试输出:
注意:
struct
(尤其是readonly
)性能比这要好得多一个类
的。感谢 @FelixUngman 和 @DavidHuxtable 的
Zip()
想法!Here is the refactored code with C# 7.2 using the
readonly struct
and theValueTuple
(alsostruct
).I use
Zip()
to create(CurrentID, PreviousID, CurrDate, PrevDate, DiffToPrev)
tuple of 5 members. It is easily iterated withforeach
:The full code:
Unit test output:
Note: the
struct
(especiallyreadonly
) performance is much better than that of aclass
.Thanks @FelixUngman and @DavidHuxtable for their
Zip()
ideas!