我有(或将要)一个返回日期序列的类(基于重复模式)。序列可能具有有限的结束(#结果或结束日期)或无限。
我想要做的是写一个类,该类将列出这样的枚举器列表(加上一个开始日期)和'组合'它们的序列分为按日期排序的一个可枚举输出序列。它必须处理结束(或甚至不启动)的源枚举,以及生成相同Date的多个源枚举。
所以我的问题是如何才能最好地实现这一目标?
更多信息(如果有帮助):
例如
如果我有RecurrencePatterns: -
(1)"每月1日,2017年7月4日结束";
(2)"每个季度结束,仅发生6次"
(3)"年度最后一天"
我从2017年1月1日开始,我希望输出为: -
2017年1月1日(1),
2017年2月1日(1),
2017年3月1日(1),
2017年3月31日(2),
2017年4月1日(1),
2017年5月1日(1),
2017年6月1日(1),
2017年6月30日(2)
2017年7月1日(1),[(1) - 最后一次列举因为下一个是在结束日期之后]
2017年9月30日(2),
2017年12月31日(2,3),[同日期的两个事件]
2018年3月31日(2),
2018年6月30日(2)[(2) - 最后一次列举,因为它只产生6]
2018年12月31日(3),
2019年12月31日(3),
2020年12月31日(3),
等
Recurrence模式类的实际输出可能是某种包含Source和Date的EventInfo类。 '组合'的输出。枚举器将类似,但是如果多个Recurrence EventInfo具有相同的日期,它将输出具有单个Date的单个EventInfo和返回该日期的源列表。
答案 0 :(得分:2)
如果要返回重复项,则应在MoreLINQ中使用SortedMerge
;如果不需要重复项,则应使用OrderedMerge
。
如果您不想安装NuGet软件包,或者没有任何覆盖符合您的需求,请按照以下方式编写。
static IEnumerable<DateTime> CombineDateTimes(IEnumerable<IEnumerable<DateTime>> list) {
// keep track of which enumerators are still available
var eligible = list.Select(l => l.GetEnumerator()).Where(l => l.MoveNext()).ToList();
while (eligible.Any()) {
// find the lowest enumerator
IEnumerator<DateTime> min = eligible.First();
foreach (var l in eligible.Skip(1)) {
if (l.Current < min.Current) {
min = l;
}
}
// here is our lowest
yield return min.Current;
if (!min.MoveNext()) {
// if the enumerator ends,
// remove it from the list of eligible enumerators
eligible = eligible.Remove(min);
}
}
}
答案 1 :(得分:1)
这样的事情,我想:
public static IEnumerable<DateTime> SortDates(IEnumerable<IEnumerable<DateTime>> iidt)
{
var enumerators = iidt.Select(iedt => iedt.GetEnumerator())
.Where(e => e.MoveNext())
.ToList();
while (enumerators.Any())
{
var lowest = enumerators.OrderBy(e => e.Current).First();
yield return lowest.Current;
if (!lowest.MoveNext())
{
enumerators.Remove(lowest);
}
}
}
答案 2 :(得分:0)
感谢Jacob和Ed的Answers,我能够提出以下代码,有人可能会觉得有用:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NUnit.Framework;
namespace TIPS.UnitTest
{
public class EventInfo
{
public DateTime Date;
public EventSource EventSource;
public List<EventSource> AdditionalEventSources; // Left null if no additional sources
}
public abstract class EventSource
{
public string Name
{
get { return GetType().Name; }
}
public abstract IEnumerable<DateTime> RecurringDates();
}
public interface IEventInfoGenerator
{
IEnumerable<EventInfo> GenerateEvents(List<EventSource> eventSources);
}
public class MyAnswerEventInfoGenerator: IEventInfoGenerator
{
public IEnumerable<EventInfo> GenerateEvents(List<EventSource> eventSources)
{
// Combine the Event Sources and their DateEnumerators ignoring any without Dates to return
var sourceEnumerators = eventSources
.Select(es =>
new
{
Source = es,
DateEnumerator = es.RecurringDates().GetEnumerator()
})
.Where(e => e.DateEnumerator.MoveNext())
.ToList();
// Keep going until there is nothing left
while (sourceEnumerators.Any())
{
// Find the earliest date
var earliestSource = sourceEnumerators.OrderBy(se => se.DateEnumerator.Current).First();
// Prepare the EventInfo
var eventInfo = new EventInfo
{
Date = earliestSource.DateEnumerator.Current,
EventSource = earliestSource.Source
};
// Remove the EventSource if it has no more Dates
if (!earliestSource.DateEnumerator.MoveNext())
{
sourceEnumerators.Remove(earliestSource);
}
// Quick check to see if there are other EventSources with the same date (no need to create a list if not necessary
if (sourceEnumerators.Any(se => se != earliestSource && se.DateEnumerator.Current == eventInfo.Date))
{
// Yes, there are so create a list for them
eventInfo.AdditionalEventSources = new List<EventSource>();
// Go through each
foreach (var additionalSourceEnumerator in sourceEnumerators.Where(se => se != earliestSource && se.DateEnumerator.Current == eventInfo.Date).ToArray())
{
// Add it to the EventInfo list
eventInfo.AdditionalEventSources.Add(additionalSourceEnumerator.Source);
// And remove them if they are spent
if (!additionalSourceEnumerator.DateEnumerator.MoveNext())
{
sourceEnumerators.Remove(additionalSourceEnumerator);
}
}
}
yield return eventInfo;
}
}
}
[TestFixture]
public class RecurrenceTests
{
static IEnumerable<EventSource> CreateTestEventSources()
{
yield return new EventSource1();
yield return new EventSource2();
yield return new EventSource3();
yield return new EmptyEventSource();
}
static void TestStackoverAnswer(IEventInfoGenerator answer)
{
var testEventSources = CreateTestEventSources().ToList();
foreach (var eventInfo in answer.GenerateEvents(testEventSources))
{
Debug.Write($"{eventInfo.Date} - {eventInfo.EventSource.Name}");
if (eventInfo.AdditionalEventSources != null)
{
Debug.Write(", " + string.Join(", ", eventInfo.AdditionalEventSources.Select(ev => ev.Name)));
}
Debug.WriteLine(string.Empty);
}
}
[Test]
public void TestMyGo()
{
TestStackoverAnswer(new MyAnswerEventInfoGenerator());
}
}
public class EventSource1: EventSource
{
public override IEnumerable<DateTime> RecurringDates()
{
yield return new DateTime(2017, 1, 1);
yield return new DateTime(2017, 2, 1);
yield return new DateTime(2017, 3, 1);
yield return new DateTime(2017, 4, 1);
yield return new DateTime(2017, 5, 1);
yield return new DateTime(2017, 6, 1);
yield return new DateTime(2017, 7, 1);
}
}
public class EventSource2: EventSource
{
public override IEnumerable<DateTime> RecurringDates()
{
yield return new DateTime(2017, 3, 31);
yield return new DateTime(2017, 6, 30);
yield return new DateTime(2017, 9, 30);
yield return new DateTime(2017, 12, 31);
}
}
public class EventSource3: EventSource
{
public override IEnumerable<DateTime> RecurringDates()
{
yield return new DateTime(2017, 12, 31);
yield return new DateTime(2018, 12, 31);
yield return new DateTime(2019, 12, 31);
}
}
public class EmptyEventSource: EventSource
{
public override IEnumerable<DateTime> RecurringDates()
{
yield break;
}
}
}
测试的输出如下: -
01/01/2017 00:00:00 - EventSource1
01/02/2017 00:00:00 - EventSource1
01/03/2017 00:00:00 - EventSource1
31/03/2017 00:00:00 - EventSource2
01/04/2017 00:00:00 - EventSource1
01/05/2017 00:00:00 - EventSource1
01/06/2017 00:00:00 - EventSource1
30/06/2017 00:00:00 - EventSource2
01/07/2017 00:00:00 - EventSource1
30/09/2017 00:00:00 - EventSource2
31/12/2017 00:00:00 - EventSource2, EventSource3
31/12/2018 00:00:00 - EventSource3
31/12/2019 00:00:00 - EventSource3