C# 合并连续日期期间
我有一个日期周期列表,这些周期没有重叠C# 合并连续日期期间,c#,date,C#,Date,我有一个日期周期列表,这些周期没有重叠 |StartDate| EndDate| | null | 1/12 | | 2/12 | null | | null | 4/12 | | 6/12 | 8/12 | | 9/12 | null | | null | 10/12 | | 11/12 | null | 我必须将这些期间合并到如下列表中: |StartDate| EndDate| | null | 1/12 | |
|StartDate| EndDate|
| null | 1/12 |
| 2/12 | null |
| null | 4/12 |
| 6/12 | 8/12 |
| 9/12 | null |
| null | 10/12 |
| 11/12 | null |
我必须将这些期间合并到如下列表中:
|StartDate| EndDate|
| null | 1/12 |
| 2/12 | 4/12 |
| 6/12 | 8/12 |
| 9/12 | 10/12 |
| 11/12 | null |
这是我的解决方案,但我认为这不是一个明智的方法
var dateList = periodList.SelectMany(x => new[] {
new {Date = x.Item1, type = "S"},
new {Date = x.Item2, type = "E"}
}).Where(x => x.Date != null).OrderBy(x => x.Date).ToArray();
var result = new List<Tuple<DateTime?, DateTime?>>();
int i = 0;
do
{
if (i == 0 && dateList[i].type == "E")
{
result.Add(new Tuple<DateTime?, DateTime?>(null, dateList[i].Date));
}
else if (i + 1 == dateList.Count() && dateList[i].type == "S")
{
result.Add(new Tuple<DateTime?, DateTime?>(dateList[i].Date, null));
}
else
{
if (dateList[i].type == "S" && dateList[i+1].type == "E")
{
result.Add(new Tuple<DateTime?, DateTime?>(dateList[i].Date, dateList[i + 1].Date));
i++;
}
}
i++;
} while (i < dateList.Count());
var dateList=periodList.SelectMany(x=>new[]{
新建{Date=x.Item1,type=“S”},
新建{Date=x.Item2,type=“E”}
}).Where(x=>x.Date!=null).OrderBy(x=>x.Date).ToArray();
var result=新列表();
int i=0;
做
{
如果(i==0&&dateList[i]。类型==“E”)
{
Add(新元组(null,日期列表[i].Date));
}
else if(i+1==dateList.Count()&&dateList[i].type==S)
{
Add(新元组(dateList[i].Date,null));
}
其他的
{
如果(日期列表[i]。类型==“S”&&dateList[i+1]。类型==“E”)
{
添加(新元组(日期列表[i].Date,日期列表[i+1].Date));
i++;
}
}
i++;
}而(i
我的解决方案可能看起来更长,但我认为它更干净。我认为你有<强> > < <强> >类(而不是<代码> tuple < /代码>)如下:
public class Period
{
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
将日期添加到期间列表中:
//准备好句点列表
List periodList=//准备期间;
通过消除null开始日期和结束日期,分别获取开始日期和结束日期:
// Get start dates
List<DateTime> startDates = periodList
.Where(p => p.StartDate.HasValue)
.Select(p => p.StartDate.Value)
.ToList();
// Get end dates
List<DateTime> endDates = periodList
.Where(p => p.EndDate.HasValue)
.Select(p => p.EndDate.Value)
.ToList();
//获取开始日期
列表开始日期=周期列表
.Where(p=>p.StartDate.HasValue)
.Select(p=>p.StartDate.Value)
.ToList();
//获取结束日期
列表结束日期=周期列表
.Where(p=>p.EndDate.HasValue)
.Select(p=>p.EndDate.Value)
.ToList();
然后执行其他操作:
// Clear list of periods
periodList.Clear();
// Add start dates which are bigger than LAST end date with NULL end date period
startDates.Where(s => s > endDates.Max())
.ToList()
.ForEach(s => periodList.Add(new Period() { StartDate = s, EndDate = null }));
// Add end dates which are smaller than FIRST start date with NULL start date period
endDates.Where(e => e < startDates.Min())
.ToList()
.ForEach(e => periodList.Add(new Period() {StartDate = null, EndDate = e}));
// Match other dates and add them to list
startDates.Where(s => s < endDates.Max())
.ToList()
.ForEach(s => periodList.Add(new Period()
{
StartDate = s,
EndDate = endDates.Where(e => e > s).Min()
}));
// Oder period list
periodList = periodList.OrderBy(p => p.StartDate).ToList();
//清除句点列表
periodList.Clear();
//添加大于最后一个结束日期且结束日期期间为空的开始日期
其中(s=>s>endDates.Max())
托利斯先生()
.ForEach(s=>periodList.Add(newperiod(){StartDate=s,EndDate=null}));
//添加小于第一个开始日期且开始日期期间为空的结束日期
其中(e=>eperiodList.Add(newperiod(){StartDate=null,EndDate=e}));
//匹配其他日期并将其添加到列表中
其中(s=>speriodList.Add(新期间()
{
起始日期=s,
EndDate=endDates.Where(e=>e>s).Min()
}));
//订货周期表
periodList=periodList.OrderBy(p=>p.StartDate.ToList();
您可以测试.NETFiddle演示
我希望它会有所帮助。下面的代码是一个使用
日期范围
类型和DateTime.MinValue
或DateTime.MaxValue
的值,以“保护”任何null
第一个开始日期或任何null
最后一个结束日期DateTime
值的平行列表中创建DateRange
值列表的方法:
public static List<DateRange> MakeRanges(List<DateTime?> list1, List<DateTime?> list2)
{
//Validate arguments to the function
if (list1 == null || list2 == null || list1.Count == 0 ||
list2.Count == 0 || list1.Count != list2.Count)
{
throw new ArgumentException("Bad arguments passed to MakeRanges().");
}
//If present, replace null start value of list1 with a sentinel
list1[0] = list1[0] ?? DateTime.MinValue;
//If present, replace null end value of list2 with a sentinel
list2[list2.Count - 1] = list2[list2.Count - 1] ?? DateTime.MaxValue;
//this expression does the heavy lifting. Match a start date with the closest non-null end-date
return list1.Where(s => s.HasValue).Select( startItem => new DateRange{
Start = startItem,
End = list2.Find(e => (e.HasValue && (e > startItem.Value))).Value
}).ToList();
}
下面是一个控制台应用程序主驱动程序:
public static void Main(string[] args)
{
List<DateTime?> list1 = new List<DateTime?>() { null, DateTime.Parse("2014-02-12"), null, DateTime.Parse("2014-06-12"), DateTime.Parse("2014-09-12"), null, DateTime.Parse("2014-11-12")};
List<DateTime?> list2 = new List<DateTime?>() { DateTime.Parse("2014-01-12"), null, DateTime.Parse("2014-04-12"), DateTime.Parse("2014-08-12"), null, DateTime.Parse("2014-10-12"), null };
List<DateRange> ranges = MakeRanges(list1, list2);
//Print out results
foreach (var range in ranges)
{
Console.WriteLine(range);
}
var pressAKeyToExit = Console.ReadKey();
}
假设输入列表中的日期总是按顺序排列,下面是一个快速解决方案(不知道是否更快):
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Linq;
名称空间SO30229368
{
班级计划
{
私有常量字符串NullItem=“null”;
静态void Main(字符串[]参数)
{
var periodList=新列表
{
新元组(NullItem,“1/12”),
新元组(“2/12”,空项),
新元组(NullItem,“4/12”),
新元组(“6/12”、“8/12”),
新元组(“9/12”,NullItem),
新元组(NullItem,“10/12”),
新元组(“11/12”,NullItem)
};
var ConcertiveList=GetConcertive(周期列表);
foreach(连续列表中的变量tupleItem)
WriteLine(“{0}-{1}”,tupleItem.Item1,tupleItem.Item2);
Console.ReadLine();
}
私有静态IEnumerable GetContinuous(列表周期列表)
{
if(periodList==null)
抛出新的ArgumentNullException(“periodList”);
如果(periodList.Count==0)
返回新列表();
var startList=periodList.Select(x=>x.Item1).Where(y=>y!=NullItem.ToList();
if(periodList.First().Item1==NullItem)
插入(0,空项);
var endList=periodList.Select(x=>x.Item2).Where(y=>y!=NullItem.ToList();
if(periodList.Last().Item2==NullItem)
endList.Add(空项);
Assert(startList.Count==endList.Count);
返回Enumerable.Zip(startList,endList,(start,end)=>newtuple(start,end)).ToList();
}
}
}
尽管正如其他人所建议的,但最好将对象建模为自定义对象(例如DateRange),而不是使用元组。如果您确定
1) 日期间隔是有序的
及
2) 每次null
endDate之后,下一个开始日期将是null
然后,一个简单的方法可能是删除所有中间null
值
List<DateTime?> startDates = allStartDates(); // excluding null values, but including the first one.
List<DateTime?> endDates = allEndDates(); // excluding null values, but including the last one.
我建议您使用属性
StartDate
和EndDate
创建一些DateRange
类,而不是使用具有无意义的Item1
和Item2
属性的元组。您还可以创建方法,如Intersects
检查时间范围是否相交,以及方法intersession
从两个范围中获取新的时间范围
public static void Main(string[] args)
{
List<DateTime?> list1 = new List<DateTime?>() { null, DateTime.Parse("2014-02-12"), null, DateTime.Parse("2014-06-12"), DateTime.Parse("2014-09-12"), null, DateTime.Parse("2014-11-12")};
List<DateTime?> list2 = new List<DateTime?>() { DateTime.Parse("2014-01-12"), null, DateTime.Parse("2014-04-12"), DateTime.Parse("2014-08-12"), null, DateTime.Parse("2014-10-12"), null };
List<DateRange> ranges = MakeRanges(list1, list2);
//Print out results
foreach (var range in ranges)
{
Console.WriteLine(range);
}
var pressAKeyToExit = Console.ReadKey();
}
null-1/12/2014
2/12/2014-4/12/2014
6/12/2014-8/12/2014
9/12/2014-10/12/2014
11/12/2014-null
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace SO30229368
{
class Program
{
private const string NullItem = "null";
static void Main(string[] args)
{
var periodList = new List<Tuple<string, string>>
{
new Tuple<string, string>(NullItem, "1/12"),
new Tuple<string, string>("2/12", NullItem),
new Tuple<string, string>(NullItem, "4/12"),
new Tuple<string, string>("6/12", "8/12"),
new Tuple<string, string>("9/12", NullItem),
new Tuple<string, string>(NullItem, "10/12"),
new Tuple<string, string>("11/12", NullItem)
};
var consecutiveList = GetConsecutive(periodList);
foreach (var tupleItem in consecutiveList)
Console.WriteLine("{0} - {1}", tupleItem.Item1, tupleItem.Item2);
Console.ReadLine();
}
private static IEnumerable<Tuple<string, string>> GetConsecutive(List<Tuple<string, string>> periodList)
{
if (periodList == null)
throw new ArgumentNullException("periodList");
if (periodList.Count == 0)
return new List<Tuple<string, string>>();
var startList = periodList.Select(x => x.Item1).Where(y => y != NullItem).ToList();
if (periodList.First().Item1 == NullItem)
startList.Insert(0, NullItem);
var endList = periodList.Select(x => x.Item2).Where(y => y != NullItem).ToList();
if (periodList.Last().Item2 == NullItem)
endList.Add(NullItem);
Debug.Assert(startList.Count == endList.Count);
return Enumerable.Zip(startList, endList, (start, end) => new Tuple<string, string>(start, end)).ToList();
}
}
}
List<DateTime?> startDates = allStartDates(); // excluding null values, but including the first one.
List<DateTime?> endDates = allEndDates(); // excluding null values, but including the last one.
DateTime? startDate3 = startDates[2];
DateTime? endDate3 = endDates[2];