当我需要对数据进行分组时,我正试图弄清楚如何计算与之前项目的差异。
我有这样的数据
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},
};
}
}
}
答案 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