我有一个数值列表,每个都有日期,如下所示:
Date Value
-------- -----
3/5/2017 2
3/6/2017 2
3/7/2017 3
3/8/2017 3
3/9/2017 3
3/10/2017 4
你可以看到我们两天都处于“2”状态,然后三天升级为“3”,第六天就升至“4”。
使用LINQ,可以很容易地显示我们获得记录的日期:
values.GroupBy(x => x.Value).OrderBy(x => x.Key).ToList().ForEach(x =>
{
var record = x.OrderBy(x1 => x1.Date).First();
Console.WriteLine(
$"New record of {x.Key} on {record.Date.ToShortDateString()}"
);
});
输出:
New record of 2 on 3/5/2017
New record of 3 on 3/7/2017
New record of 4 on 3/10/2017
这很棒,但如果我想这样做会怎么样:
New record of 2 on 3/5/2017
New record of 3 on 3/7/2017 (took 2 days)
New record of 4 on 3/10/2017 (took 3 days)
该ForEach
循环的每次迭代都必须知道最后一次迭代的值才能计算差值。这怎么可能?
答案:
下面选择了答案,但这是我使用Aggregate
的实际实现:
values.OrderBy(x => x.Date).Aggregate((a, b) =>
{
if (b.Value > a.Value)
{
$"New record of {b.Value} on {b.Date.ToShortDateString()} (took {b.Date.Subtract(a.Date).Days} day(s))".Dump();
return b;
}
return a;
});
结果:
New record of 3 on 3/7/2017 (took 2 day(s))
New record of 4 on 3/10/2017 (took 3 day(s))
请注意,那里没有列出2的“基线”,这对我来说很好。
Aggregate
的关键在于它可以通过二进制中的枚举来编写功能。所以:
1,2
2,3
3,4
在许多情况下,您将这两件事合并,然后返回组合。但是没有理由你不能只是比较它们,并返回其中一个。这就是我做的 - 我比较了它们,然后如果它 是新记录则返回新记录,否则我返回现有记录。
答案 0 :(得分:2)
请考虑聚合:
values.Aggregate(new Tuple<int,int?>(0,null), (acc, e) =>
{
if(acc.Item2==null)
{
Console.WriteLine($"New record of {e.Value} on {e.Date.ToShortDateString()}");
return new Tuple<int, int?>(1, e.Value);
}
else
{
if(e.Value!=acc.Item2.Value)
{
Console.WriteLine($"New record of {e.Value} on {e.Date.ToShortDateString()} (took {acc.Item1} days)");
return new Tuple<int, int?>(1, e.Value);
}
else
{
return new Tuple<int, int?>(acc.Item1+1, acc.Item2);
}
}
});
答案 1 :(得分:1)
您的LINQ查询中有一些冗余语句,这可能会导致性能问题。幸运的是,尽管你没有LINQ所有的东西。好的'ol foreach
仍然有用:
//Iterate through all groups
foreach(var group in values.GroupBy(x => x.Value))
{
//sort the records by date
var records = group.OrderBy(x => x.Date).ToList();
//Grab the first record
var firstRecord = records.First();
if(records.Count > 1)
{
//Gets diff in days between first and last
int dayCount = (firstRecord.Date - records.Last()).TotalDays;
Console.WriteLine($"New record of {firstRecord.Key} on {firstRecord .Date.ToShortDateString()} took {dayCount} days");
}
else
{
Console.WriteLine($"New record of {firstRecord.Key} on {firstRecord .Date.ToShortDateString()}");
}
}