C# - 将具有日期的行分组为具有持续时间的块

时间:2014-04-08 15:49:08

标签: c# sql

这有点难以解释。我有一个包含日程安排信息的数据表。每行代表一个包含开始日期/时间和结束日期/时间的计划。我需要对这些进行分组,以使整个开始时间与给定的持续时间匹配。

例如,我的数据表中可能包含以下内容:

Schedule1: Start - 9:00AM, End - 9:30AM
Schedule2: Start - 9:30AM, End - 10:00AM
Schedule3: Start - 10:00AM, End - 10:30AM
Schedule4: Start - 10:30AM, End - 11:00AM

现在,如果我的持续时间值为60分钟,那么我需要能够产生以下输出:

Block1: Schedules(1,2): 9:00AM - 10:00AM
Block2: Schedules(2,3): 9:30AM - 10:30AM
Block3: Schedules(3,4): 10:00AM - 11:00AM

但是如果持续时间是120分钟,那么我需要产生以下内容:

Block1: Schedules(1,2,3,4): 9:00AM - 11:00AM

如果需要澄清,请告诉我。我需要在C#中编写一个方法来执行此转换。请帮助我,因为我已经坚持了很长时间。

1 个答案:

答案 0 :(得分:1)

您是否选择在C#或SQL中执行此操作,部分取决于数据的规模。假设我们使用相对较少的时间范围(比如< 10),那么将所有时间都拉入内存并在C#中找到块是合理的。

鉴于以下类别:

public class Schedule {
    public int ID { get; set; }
    public DateTime Start { get; set; }
    public DateTime End { get; set; }
    public int Minutes { get; set; }
}

public class ScheduleBlock : Schedule {
    public List<Schedule> Schedules { get; set; }
}

这是一个简单的算法,迭代地将范围组合在一起,直到表示每个可能的组合(注意组合的数量增长为O(n ^ 2)):

public List<ScheduleBlock> CombineAllSchedules(List<Schedule> origschedules, out int added)
{
    added = 0;
    var schedules = new List<ScheduleBlock>();
    foreach (var s in origschedules) {
        var snew = new ScheduleBlock { Schedules = new List<Schedule> { s }, Start = s.Start, End = s.End, Minutes = s.Minutes };
        schedules.Add(snew);
    }

    for (var i = 0; i < schedules.Count; i++) {
        var s = schedules[i];
        var matchstart = schedules.Where (s2 => s2.End == s.Start).ToList();
        var matchend = schedules.Where (s2 => s2.Start == s.End).ToList();
        foreach (var s2 in matchstart) {
            var newschedule = CombineSchedules(s2, s);
            if (!schedules.Any (sc => sc.Start == newschedule.Start && sc.End == newschedule.End)) {
                schedules.Add(newschedule);
                added++;
            }
        }

        foreach (var s2 in matchend) {
            var newschedule = CombineSchedules(s, s2);
            if (!schedules.Any (sc => sc.Start == newschedule.Start && sc.End == newschedule.End)) {
                schedules.Add(newschedule);
                added++;
            }
        }
    }
    return schedules;
}

public ScheduleBlock CombineSchedules(Schedule s1, Schedule s2)
{
    var schedules = new List<Schedule>();
    if (s1 is ScheduleBlock) schedules.AddRange(((ScheduleBlock)s1).Schedules);
    else schedules.Add(s1);
    if (s2 is ScheduleBlock) schedules.AddRange(((ScheduleBlock)s2).Schedules);
    else schedules.Add(s2);
    var s = new ScheduleBlock {
        Schedules = schedules,
        Start = s1.Start, End = s2.End, Minutes = s1.Minutes + s2.Minutes
    };
    return s;
}

将组合放在一起后,查询它们并获得特定长度(如60分钟或120分钟)是一件简单的事情:

public List<ScheduleBlock> FindBlocks(List<Schedule> schedules, int blockLength)
{
    int added;
    var combinedSchedules = CombineAllSchedules(schedules, out added);
    var result = combinedSchedules.Where (s => s.Minutes == blockLength).ToList();
    return result;
}

有了这个算法,你可以做这样的事情,例如获得你正在寻找的输出:

var schedules = new List<Schedule> {
    new Schedule { ID = 1, Start = DateTime.Parse("09:00 AM"), End = DateTime.Parse("09:30 AM") },
    new Schedule { ID = 2, Start = DateTime.Parse("09:30 AM"), End = DateTime.Parse("10:00 AM") },
    new Schedule { ID = 3, Start = DateTime.Parse("10:00 AM"), End = DateTime.Parse("10:30 AM") },
    new Schedule { ID = 4, Start = DateTime.Parse("10:30 AM"), End = DateTime.Parse("11:00 AM") },
};

foreach (var s in schedules) {
    s.Minutes = (int)(s.End - s.Start).TotalMinutes;
}

Console.WriteLine("60 Minute Blocks");
Console.WriteLine("----------------");
var blocks = FindBlocks(schedules, 60);
var blockId = 1;
foreach (var block in blocks) {
    var output = "Block" + blockId + 
        ": Schedules(" + string.Join(",", block.Schedules.Select (s => s.ID)) + "): " +
        block.Start.ToString("h:mmtt") + " - " + block.End.ToString("h:mmtt");
    Console.WriteLine(output);
    blockId++;
}

Console.WriteLine();
Console.WriteLine("120 Minute Blocks");
Console.WriteLine("----------------");
blocks = FindBlocks(schedules, 120);
blockId = 1;
foreach (var block in blocks) {
    var output = "Block" + blockId + 
        ": Schedules(" + string.Join(",", block.Schedules.Select (s => s.ID)) + "): " +
        block.Start.ToString("h:mmtt") + " - " + block.End.ToString("h:mmtt");
    Console.WriteLine(output);
    blockId++;
}

示例结果:

60 Minute Blocks
----------------
Block1: Schedules(1,2): 9:00AM - 10:00AM
Block2: Schedules(2,3): 9:30AM - 10:30AM
Block3: Schedules(3,4): 10:00AM - 11:00AM

120 Minute Blocks
----------------
Block1: Schedules(1,2,3,4): 9:00AM - 11:00AM