使用(单个)LINQ查询计算组中先前项目的差异

时间:2010-11-21 10:57:21

标签: c# linq

当我需要对数据进行分组时,我正试图弄清楚如何计算与之前项目的差异。

我有这样的数据

City      Area  Date        Citizens
New York    1   2010.11.20  5
New York    1   2010.11.21  8
New York    1   2010.11.22  12
New York    1   2010.11.23  17
New York    1   2010.11.24  23
New York    1   2010.11.25  29

Chicago 1   2010.11.20  5
Chicago 1   2010.11.21  10
Chicago 1   2010.11.22  15
Chicago 1   2010.11.23  20
Chicago 1   2010.11.24  25
Chicago 1   2010.11.25  30

New York    2   2010.11.20  6
New York    2   2010.11.21  7
New York    2   2010.11.22  9
New York    2   2010.11.23  7
New York    2   2010.11.24  10
New York    2   2010.11.25  15

Chicago 2   2010.11.20  5
Chicago 2   2010.11.21  15
Chicago 2   2010.11.22  25
Chicago 2   2010.11.23  20
Chicago 2   2010.11.24  25
Chicago 2   2010.11.25  30

我需要为每个城市添加一个“增加”列,这可以通过从当前减去以前的公民数来计算。

The expected result is like this

City    Area    Date    Citizens    Increase
New York    1   2010.11.20  5   5
New York    1   2010.11.21  8   3
New York    1   2010.11.22  12  4
New York    1   2010.11.23  17  5
New York    1   2010.11.24  23  6
New York    1   2010.11.25  29  7

Chicago 1   2010.11.20  5   5
Chicago 1   2010.11.21  10  5
Chicago 1   2010.11.22  15  5
Chicago 1   2010.11.23  20  5
Chicago 1   2010.11.24  25  5
Chicago 1   2010.11.25  30  5

New York    2   2010.11.20  6   6
New York    2   2010.11.21  7   1
New York    2   2010.11.22  9   2
New York    2   2010.11.23  7   -2
New York    2   2010.11.24  10  3
New York    2   2010.11.25  15  5

Chicago 2   2010.11.20  5   5
Chicago 2   2010.11.21  15  10
Chicago 2   2010.11.22  25  10
Chicago 2   2010.11.23  20  -5
Chicago 2   2010.11.24  25  5
Chicago 2   2010.11.25  30  5

我想知道是否可以使用单个linq查询完成此操作,避免 “foreach(c在城市)     foreach(a in area)        ......“

问题是如何计算行“7”,其中简单地差异到前一个记录将是-24,何时应该是5.

以下是示例代码:

using System;
using System.Collections.Generic;

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

    class Program
    {

        static void Main()
        {
            var list = new List<MyObject>
              {
                new MyObject {ID= 1, City = "New York",Date = DateTime.Now,          Value = 5},
                new MyObject {ID= 1, City = "New York",Date = DateTime.Now.AddDays(1),Value = 8},
                new MyObject {ID= 1, City = "New York",Date = DateTime.Now.AddDays(2),Value = 12},
                new MyObject {ID= 1, City = "New York",Date = DateTime.Now.AddDays(3),Value = 17},
                new MyObject {ID= 1, City = "New York",Date = DateTime.Now.AddDays(4),Value = 23},
                new MyObject {ID= 1, City = "New York",Date = DateTime.Now.AddDays(5),Value = 29},

                new MyObject {ID= 1, City = "Chicago",Date = DateTime.Now,           Value = 5},
                new MyObject {ID= 1, City = "Chicago",Date = DateTime.Now.AddDays(1),Value = 10},
                new MyObject {ID= 1, City = "Chicago",Date = DateTime.Now.AddDays(2),Value = 15},
                new MyObject {ID= 1, City = "Chicago",Date = DateTime.Now.AddDays(3),Value = 20},
                new MyObject {ID= 1, City = "Chicago",Date = DateTime.Now.AddDays(4),Value = 25},
                new MyObject {ID= 1, City = "Chicago",Date = DateTime.Now.AddDays(5),Value = 30},

                new MyObject {ID= 2, City = "New York",Date = DateTime.Now,          Value = 6},
                new MyObject {ID= 2, City = "New York",Date = DateTime.Now.AddDays(1),Value = 7},
                new MyObject {ID= 2, City = "New York",Date = DateTime.Now.AddDays(2),Value = 9},
                new MyObject {ID= 2, City = "New York",Date = DateTime.Now.AddDays(3),Value = 7},
                new MyObject {ID= 2, City = "New York",Date = DateTime.Now.AddDays(4),Value = 10},
                new MyObject {ID= 2, City = "New York",Date = DateTime.Now.AddDays(5),Value = 15},

                new MyObject {ID= 2, City = "Chicago",Date = DateTime.Now,           Value = 5},
                new MyObject {ID= 2, City = "Chicago",Date = DateTime.Now.AddDays(1),Value = 15},
                new MyObject {ID= 2, City = "Chicago",Date = DateTime.Now.AddDays(2),Value = 25},
                new MyObject {ID= 2, City = "Chicago",Date = DateTime.Now.AddDays(3),Value = 20},
                new MyObject {ID= 2, City = "Chicago",Date = DateTime.Now.AddDays(4),Value = 25},
                new MyObject {ID= 2, City = "Chicago",Date = DateTime.Now.AddDays(5),Value = 30},               
            };

        }
    }
}

3 个答案:

答案 0 :(得分:4)

你可以这样做:

var newList = list.GroupBy(x => new { x.City, x.ID })
.Select
(
    x =>
    {
        var subList = x.OrderBy(y => y.Date).ToList();
        return subList.Select((y, idx) => new MyObject
        {
            ID = y.ID,
            City = y.City,
            Date = y.Date,
            Value = y.Value,
            DiffToPrev = (idx == 0) ? y.Value : y.Value - subList.ElementAt(idx-1).Value 
        });
    }
)
.SelectMany(x => x)
.ToList();

无论如何,我认为在这种情况下,foreach声明更清晰(而不是更长),例如:

List<MyObject> newList = new List<MyObject>();
foreach (var grp in list.GroupBy(x => new { x.City, x.ID }))
{
    MyObject prev = null;
    foreach (var obj in grp.OrderBy(y => y.Date))
    {
        newList.Add(new MyObject
        {
            ID = obj.ID,
            City = obj.City,
            Date = obj.Date,
            Value = obj.Value,
            DiffToPrev = (prev == null) ? obj.Value : obj.Value - prev.Value
        });
        prev = obj;
    }
}

<强> P.S。
显然(取决于您的需求)在foreach代码中,您可以直接在现有对象(DiffToPrev)上设置obj,而不是创建新对象,从而可选择创建newList


<强>结果:

ID: 1,City: New York,Date: 21/11/2010 12:52:40,Value: 5,Diff: 5
ID: 1,City: New York,Date: 22/11/2010 12:52:40,Value: 8,Diff: 3
ID: 1,City: New York,Date: 23/11/2010 12:52:40,Value: 12,Diff: 4
ID: 1,City: New York,Date: 24/11/2010 12:52:40,Value: 17,Diff: 5
ID: 1,City: New York,Date: 25/11/2010 12:52:40,Value: 23,Diff: 6
ID: 1,City: New York,Date: 26/11/2010 12:52:40,Value: 29,Diff: 6
ID: 1,City: Chicago,Date: 21/11/2010 12:52:40,Value: 5,Diff: 5
ID: 1,City: Chicago,Date: 22/11/2010 12:52:40,Value: 10,Diff: 5
ID: 1,City: Chicago,Date: 23/11/2010 12:52:40,Value: 15,Diff: 5
ID: 1,City: Chicago,Date: 24/11/2010 12:52:40,Value: 20,Diff: 5
ID: 1,City: Chicago,Date: 25/11/2010 12:52:40,Value: 25,Diff: 5
ID: 1,City: Chicago,Date: 26/11/2010 12:52:40,Value: 30,Diff: 5
ID: 2,City: New York,Date: 21/11/2010 12:52:40,Value: 6,Diff: 6
ID: 2,City: New York,Date: 22/11/2010 12:52:40,Value: 7,Diff: 1
ID: 2,City: New York,Date: 23/11/2010 12:52:40,Value: 9,Diff: 2
ID: 2,City: New York,Date: 24/11/2010 12:52:40,Value: 7,Diff: -2
ID: 2,City: New York,Date: 25/11/2010 12:52:40,Value: 10,Diff: 3
ID: 2,City: New York,Date: 26/11/2010 12:52:40,Value: 15,Diff: 5
ID: 2,City: Chicago,Date: 21/11/2010 12:52:40,Value: 5,Diff: 5
ID: 2,City: Chicago,Date: 22/11/2010 12:52:40,Value: 15,Diff: 10
ID: 2,City: Chicago,Date: 23/11/2010 12:52:40,Value: 25,Diff: 10
ID: 2,City: Chicago,Date: 24/11/2010 12:52:40,Value: 20,Diff: -5
ID: 2,City: Chicago,Date: 25/11/2010 12:52:40,Value: 25,Diff: 5
ID: 2,City: Chicago,Date: 26/11/2010 12:52:40,Value: 30,Diff: 5
ID: 1,City: New York,Date: 21/11/2010 12:52:40,Value: 5,Diff: 5
ID: 1,City: New York,Date: 22/11/2010 12:52:40,Value: 8,Diff: 3
ID: 1,City: New York,Date: 23/11/2010 12:52:40,Value: 12,Diff: 4
ID: 1,City: New York,Date: 24/11/2010 12:52:40,Value: 17,Diff: 5
ID: 1,City: New York,Date: 25/11/2010 12:52:40,Value: 23,Diff: 6
ID: 1,City: New York,Date: 26/11/2010 12:52:40,Value: 29,Diff: 6
ID: 1,City: Chicago,Date: 21/11/2010 12:52:40,Value: 5,Diff: 5
ID: 1,City: Chicago,Date: 22/11/2010 12:52:40,Value: 10,Diff: 5
ID: 1,City: Chicago,Date: 23/11/2010 12:52:40,Value: 15,Diff: 5
ID: 1,City: Chicago,Date: 24/11/2010 12:52:40,Value: 20,Diff: 5
ID: 1,City: Chicago,Date: 25/11/2010 12:52:40,Value: 25,Diff: 5
ID: 1,City: Chicago,Date: 26/11/2010 12:52:40,Value: 30,Diff: 5
ID: 2,City: New York,Date: 21/11/2010 12:52:40,Value: 6,Diff: 6
ID: 2,City: New York,Date: 22/11/2010 12:52:40,Value: 7,Diff: 1
ID: 2,City: New York,Date: 23/11/2010 12:52:40,Value: 9,Diff: 2
ID: 2,City: New York,Date: 24/11/2010 12:52:40,Value: 7,Diff: -2
ID: 2,City: New York,Date: 25/11/2010 12:52:40,Value: 10,Diff: 3
ID: 2,City: New York,Date: 26/11/2010 12:52:40,Value: 15,Diff: 5
ID: 2,City: Chicago,Date: 21/11/2010 12:52:40,Value: 5,Diff: 5
ID: 2,City: Chicago,Date: 22/11/2010 12:52:40,Value: 15,Diff: 10
ID: 2,City: Chicago,Date: 23/11/2010 12:52:40,Value: 25,Diff: 10
ID: 2,City: Chicago,Date: 24/11/2010 12:52:40,Value: 20,Diff: -5
ID: 2,City: Chicago,Date: 25/11/2010 12:52:40,Value: 25,Diff: 5
ID: 2,City: Chicago,Date: 26/11/2010 12:52:40,Value: 30,Diff: 5

答案 1 :(得分:1)

使用Reactive extensions的解决方案:

var zero = EnumerableEx.Return(new MyObject { Value = 0 });
var result = list
    .GroupBy(o => new { o.City, o.ID })
    .SelectMany(g => g
        .Zip(zero.Concat(g), (o1, o2) =>
        new { O = o1, Diff = o1.Value - o2.Value }));

您需要引用System.Interactive

重新使用MyObject的另一个版本:

var result = list
    .GroupBy(o => new { o.City, o.ID })
    .SelectMany(g => g
        .Zip(zero.Concat(g), (o1, o2) =>
            new MyObject {
                ID = o1.ID, City = o1.City, Date = o1.Date, Value = o1.Value, DiffToPrev = o1.Value - o2.Value
            }));

答案 2 :(得分:1)

试试这个:

list.OrderBy (l => l.Date)
    .GroupBy (l => new { l.ID, l.City })
    .ToList()
    .ForEach(g => g.Aggregate (0, (acc, m)  => { m.DiffToPrev = m.Value - acc; return m.Value; }));

在这里,我按日期排序,以防它们过时,然后按ID和城市分组。将其转换为List,然后对于每个组,使用Aggregate函数设置与组中先前值的差异。

您可以在ideone中看到结果:

ID: 1, City: New York, Date: 11/21/2010 9:01:30 AM, Value: 5, DiffToPrev: 5
ID: 1, City: New York, Date: 11/22/2010 9:01:30 AM, Value: 8, DiffToPrev: 3
ID: 1, City: New York, Date: 11/23/2010 9:01:30 AM, Value: 12, DiffToPrev: 4
ID: 1, City: New York, Date: 11/24/2010 9:01:30 AM, Value: 17, DiffToPrev: 5
ID: 1, City: New York, Date: 11/25/2010 9:01:30 AM, Value: 23, DiffToPrev: 6
ID: 1, City: New York, Date: 11/26/2010 9:01:30 AM, Value: 29, DiffToPrev: 6
ID: 1, City: Chicago, Date: 11/21/2010 9:01:30 AM, Value: 5, DiffToPrev: 5
ID: 1, City: Chicago, Date: 11/22/2010 9:01:30 AM, Value: 10, DiffToPrev: 5
ID: 1, City: Chicago, Date: 11/23/2010 9:01:30 AM, Value: 15, DiffToPrev: 5
ID: 1, City: Chicago, Date: 11/24/2010 9:01:30 AM, Value: 20, DiffToPrev: 5
ID: 1, City: Chicago, Date: 11/25/2010 9:01:30 AM, Value: 25, DiffToPrev: 5
ID: 1, City: Chicago, Date: 11/26/2010 9:01:30 AM, Value: 30, DiffToPrev: 5
ID: 2, City: New York, Date: 11/21/2010 9:01:30 AM, Value: 6, DiffToPrev: 6
ID: 2, City: New York, Date: 11/22/2010 9:01:30 AM, Value: 7, DiffToPrev: 1
ID: 2, City: New York, Date: 11/23/2010 9:01:30 AM, Value: 9, DiffToPrev: 2
ID: 2, City: New York, Date: 11/24/2010 9:01:30 AM, Value: 7, DiffToPrev: -2
ID: 2, City: New York, Date: 11/25/2010 9:01:30 AM, Value: 10, DiffToPrev: 3
ID: 2, City: New York, Date: 11/26/2010 9:01:30 AM, Value: 15, DiffToPrev: 5
ID: 2, City: Chicago, Date: 11/21/2010 9:01:30 AM, Value: 5, DiffToPrev: 5
ID: 2, City: Chicago, Date: 11/22/2010 9:01:30 AM, Value: 15, DiffToPrev: 10
ID: 2, City: Chicago, Date: 11/23/2010 9:01:30 AM, Value: 25, DiffToPrev: 10
ID: 2, City: Chicago, Date: 11/24/2010 9:01:30 AM, Value: 20, DiffToPrev: -5
ID: 2, City: Chicago, Date: 11/25/2010 9:01:30 AM, Value: 25, DiffToPrev: 5
ID: 2, City: Chicago, Date: 11/26/2010 9:01:30 AM, Value: 30, DiffToPrev: 5