给出单日日期(dd-mm-yyyy)的数据表(如下左图所示)。
将此转换为范围集合的最优雅方式是,为每种类型分组连续几天(在右侧显示)?
我们可以假设初始数据按TypeID排序,然后按日期排序。
TypeID | Date -> TypeID | Start | End
1 | 01/02/2010 1 | 01/02/2010 | 03/02/2010
1 | 02/02/2010 2 | 03/02/2010 | 04/02/2010
1 | 03/02/2010 2 | 06/02/2010 | 06/02/2010
2 | 03/02/2010
2 | 04/02/2010
2 | 06/02/2010
我对LINQ并不特别,但我认为这可能是要走的路?
非常感谢任何帮助!
答案 0 :(得分:2)
当然有人会提出一个简洁的LINQ解决方案,但这是我的旧式解决方案。当然它有问题,不会应付所有组合,拼凑在一起解决问题,但不以任何方式优雅: - (
internal class SourceData
{
public int TypeId { get; set; }
public DateTime Date { get; set; }
}
internal class Result
{
public int TypeId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
class Program
{
static void Main()
{
var a = new List<SourceData> {
new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 01)},
new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 02)},
new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 03)},
new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 03)},
new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 04)},
new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 06)}
};
var results = new List<Result>();
int currentTypeId = 1;
var rangeEndDate = new DateTime();
DateTime rangeStartDate = a[0].Date;
DateTime currentDate = a[0].Date;
for (int i = 1; i < a.Count() ; i++)
{
if (a[i].TypeId != currentTypeId)
{
results.Add(new Result() { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = rangeEndDate });
currentTypeId += 1;
rangeStartDate = a[i].Date;
}
TimeSpan tSpan = a[i].Date - currentDate;
int differenceInDays = tSpan.Days;
if(differenceInDays > 1)
{
results.Add(new Result { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = a[i-1].Date });
rangeStartDate = a[i].Date;
}
rangeEndDate = a[i].Date;
currentDate = a[i].Date;
}
results.Add(new Result { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = rangeEndDate });
Console.WriteLine("Output\n");
foreach (var r in results)
Console.WriteLine( string.Format( "{0} - {1} - {2}",r.TypeId,r.StartDate.ToShortDateString(),r.EndDate.ToShortDateString()));
Console.ReadLine();
}
}
提供以下输出: -
输出
1 - 01/02/2010 - 03/02/2010
2 - 03/02/2010 - 04/02/2010
2 - 06/02/2010 - 06/02/2010
答案 1 :(得分:2)
注意以前的答案已删除。
编辑尝试修改后的这个:
public static IEnumerable<TAnonymous> Flatten<T, TAnonymous>(
this IEnumerable<T> enumerable,
Func<T, T, bool> criteria,
Func<T, T, TAnonymous> selector,
Func<TAnonymous, T, T, TAnonymous> modifier)
{
var list = new List<TAnonymous>();
T last = default(T);
bool first = true;
bool created = false;
TAnonymous current = default(TAnonymous);
Action<T, T> action = (a, b) =>
{
if (criteria(a, b)) {
if (created) {
current = modifier(current, a, b);
} else {
current = selector(a, b);
created = true;
}
} else {
if (created) {
list.Add(current);
current = default(TAnonymous);
created = false;
} else {
list.Add(selector(a, a));
}
}
};
foreach (T item in enumerable) {
if (first) {
first = false;
last = item;
continue;
}
action(last, item);
last = item;
}
action(last, last);
if (created)
list.Add(current);
return list;
}
被称为:
var filtered = list.Flatten(
(r1, r2) => ((r2.Date - r1.Date).Days <= 1 && r1.TypeID == r2.TypeID),
(r1, r2) => new { r1.TypeID, Start = r1.Date, End = r2.Date },
(anon, r1, r2) => new { anon.TypeID, anon.Start, End = r2.Date });
应该有希望工作......这次我们正在做的是将操作分成几个阶段,首先匹配一个标准,然后创建一个新项目(选择器),或更新以前创建的项目(修饰符)。