C# 如何创建特殊的“组合”枚举数
我已经有或将有一个类,它返回基于重复模式的日期序列。序列可能有有限的结束结果或结束日期,也可能是无限的 我要做的是编写一个类,它将获取此类枚举数的列表加上开始日期,并将它们的序列“组合”成一个按日期排序的可枚举输出序列。它必须处理结束或甚至不开始的源枚举,以及产生相同日期的多个源枚举 所以我的问题是,我如何才能最好地实现这一点 更多信息(如果有帮助): 例如 如果我有以下复发模式:- 每月1日,截止2017年7月4日; 每季度末2次,仅6次 3年的最后一天 从2017年1月1日开始,我希望输出为:- 1971年1月1日, 2017年2月1日, 2017年3月1日, 2017年3月31日2, 2017年4月1日, 2017年5月1日, 2017年6月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, 等等 递归模式类的实际输出可能是某种包含源和日期的EventInfo类。 “组合”枚举数的输出类似,但如果多个重复事件信息具有相同的日期,它将输出一个带有单个日期的单个事件信息以及返回该日期的源列表。如果希望返回重复项,或者不需要重复项,则应在MoreLINQ中使用 如果您不想安装NuGet软件包,或者所有覆盖都不适合您的需要,您可以这样编写C# 如何创建特殊的“组合”枚举数,c#,ienumerable,C#,Ienumerable,我已经有或将有一个类,它返回基于重复模式的日期序列。序列可能有有限的结束结果或结束日期,也可能是无限的 我要做的是编写一个类,它将获取此类枚举数的列表加上开始日期,并将它们的序列“组合”成一个按日期排序的可枚举输出序列。它必须处理结束或甚至不开始的源枚举,以及产生相同日期的多个源枚举 所以我的问题是,我如何才能最好地实现这一点 更多信息(如果有帮助): 例如 如果我有以下复发模式:- 每月1日,截止2017年7月4日; 每季度末2次,仅6次 3年的最后一天 从2017年1月1日开始,我希望输出为
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);
}
}
}
我想是这样的:
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);
}
}
}
感谢Jacob和Ed的回答,我能够想出以下代码,可能会有人觉得有用:
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;
}
}
您是说要合并多个非终止枚举的输出。。。排序?对第一个问题是肯定的,对第二个问题是“排序”——排序将在每个来源返回的单个日期上进行——然后产生最早的日期,并按顺序向其来源询问下一个日期。进程重复。好的,您可以从每个枚举上的GetEnumerator开始,将它们放入列表中,然后在每个枚举上移动Next。在主循环中,检查每个枚举数上的Current,在值最低的枚举数上返回lowest和MoveNext,重复?多亏了Ed和Jacob,我现在可以看到如何使用Current和MoveNext来实现结果。Jacob的代码是第一个,有注释,但最初没有编译;仅仅几分钟后,Ed's就可以直接使用,而且读起来稍微干净一些。但是:-,两者都不处理产生相同日期的不同枚举数。可能一开始我没有解释清楚,但更多的信息显示12月31日应该只输出一次。“我认为最公平的做法是把两个答案都投上一票。”“雅各布·克莱尔是的,我看到你的答案出现了,就转移了视线,直到我写完我的答案。洁净室,我发誓!我相信你:在我发布我的评论几秒钟后,我看到了你最初的评论。
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