LINQ中的ForEach循环如何知道循环的先前迭代中的值?

时间:2017-07-19 20:21:19

标签: c# linq

我有一个数值列表,每个都有日期,如下所示:

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

在许多情况下,您将这两件事合并,然后返回组合。但是没有理由你不能只是比较它们,并返回其中一个。这就是我做的 - 我比较了它们,然后如果它 是新记录则返回新记录,否则我返回现有记录。

2 个答案:

答案 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()}");
    }        
}