我有一个日期期间列表,这些期间不重叠
|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());
答案 0 :(得分:0)
我的解决方案似乎更长,但在我看来更清晰 。我认为您有期间类(而不是Tuple<DateTime?, DateTime?>
),如下所示:
public class Period
{
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
将您的日期添加到期间列表中:
// Make list of periods ready
List<Period> periodList = //Prepare periods;
通过消除 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();
然后做其他操作:
// 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();
您可以测试.NET小提琴演示here。
我希望它会有所帮助。
答案 1 :(得分:0)
以下代码是使用
的实现DateRange
类型和DateTime.MinValue
或DateTime.MaxValue
的标记值,以“保护”任何null
首次开始日期或任何null
最后结束日期。以下是从可以为DateRange
个值的两个并行列表中创建DateTime
值列表的方法:
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();
}
这是一个清理任何标记的DateRange类:
public class DateRange
{
private DateTime? _start;
private DateTime? _end;
public DateTime? Start
{
get { return _start; }
//get rid of any sentinel value
set { _start = value == DateTime.MinValue ? null : value; }
}
public DateTime? End
{
get { return _end; }
// get rid of any sentinel value
set { _end = value == DateTime.MaxValue ? null : value; }
}
//For checking results
public override string ToString()
{
return String.Format("{0}-{1}", Start.HasValue ? Start.Value.ToShortDateString() : "null", End.HasValue ? End.Value.ToShortDateString() : "null");
}
}
这是一个Console App Main作为驱动程序:
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
答案 2 :(得分:0)
假设日期总是在输入列表中按顺序排列,以下可以是一个快速解决方案(不要知道它是否更快):
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();
}
}
}
虽然按照其他人的建议,最好将对象建模为自定义对象(例如DateRange),而不是使用元组。
答案 3 :(得分:0)
如果你确定
1)日期间隔按顺序
和
2)在每个null
endDate之后,下一个startDate将为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.
值。
DateTime? startDate3 = startDates[2];
DateTime? endDate3 = endDates[2];
然后你可以进行一对一的比赛以获得所有时间间隔。例如;
.../sdk/platform-tools/adb install -r .../build/outputs/apk/app-debug.apk