如何创建一个特殊的组合'枚举

时间:2017-01-20 17:37:33

标签: c# ienumerable

我有(或将要)一个返回日期序列的类(基于重复模式)。序列可能具有有限的结束(#结果或结束日期)或无限。

我想要做的是写一个类,该类将列出这样的枚举器列表(加上一个开始日期)和'组合'它们的序列分为按日期排序的一个可枚举输出序列。它必须处理结束(或甚至不启动)的源枚举,以及生成相同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和返回该日期的源列表。

3 个答案:

答案 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