6

我目前正在开发一个 C# 程序,我需要组合一堆时间范围。对于每个范围,我都有开始和结束时间。我发现了一个例子,这是在 Ruby 中完成的,但不是在 C# 中完成的。我基本上是在寻找时间范围联盟。我觉得可能有一种方法可以使用 linq 来做到这一点,但我想不出它。有任何想法吗?

所以例如

开始时间:1:30 结束时间:2:00

开始时间:1:45 结束时间:2:30

开始时间:3:00 结束时间:5:00

开始时间:4:00 结束时间:4:30

开始时间:4:45 结束时间:5:30

这组时间将作为

开始时间:1:30 结束时间:2:30

开始时间:3:00 结束时间:5:30

4

3 回答 3

8

你可以看看这个支持TimeRanges和交集方法的项目:

http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET

在此处输入图像描述

于 2012-10-29T21:54:45.317 回答
6

这看起来很有趣,所以我开始编写一些代码。

public class TimeRanges
{
    private List<TimeRange> _mergedTimeRanges = new List<TimeRange>();

    public void Add(TimeRange timeRange)
    {
        if(!_mergedTimeRanges.Any(x=>x.IsOverLap(timeRange)))
        {
            _mergedTimeRanges.Add(timeRange);
            return;
        }
        while (_mergedTimeRanges.Any(x => x.IsOverLap(timeRange) && x!=timeRange))
        {
            TimeRange toMergeRange = _mergedTimeRanges.First(x => x.IsOverLap(timeRange));
            toMergeRange.Merge(timeRange);
            timeRange = toMergeRange;
        }
    }

    public IEnumerable<TimeRange> GetMergedRanges()
    {
        return _mergedTimeRanges;
    }
}
public class TimeRange
{
    public DateTime Start { get; private set; }
    public DateTime End { get; private set; }
    public TimeRange(DateTime start, DateTime end)
    {
        if (start >= end)
            throw new ArgumentException("Invalid time range, end must be later than start");
        Start = start;
        End = end;
    }

    public void Merge(TimeRange timeRange)
    {
        if (!IsOverLap(timeRange))
            throw new ArgumentException("Cannot merge timeranges that don't overlap", "timeRange");
        if (End < timeRange.End)
            End = timeRange.End;
        if (timeRange.Start < Start)
            Start = timeRange.Start;
    }

    public bool IsOverLap(TimeRange timeRange)
    {
        if (timeRange.End < Start)
            return false;
        if (timeRange.Start > End)
            return false;
        return true;
    }

    public bool Equals(TimeRange other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return other.Start.Equals(Start) && other.End.Equals(End);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (TimeRange)) return false;
        return Equals((TimeRange) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (Start.GetHashCode()*397) ^ End.GetHashCode();
        }
    }
}

如果有人感兴趣,我有几个测试

于 2012-10-29T23:06:51.103 回答
3

Johan Larsson 的回答对我帮助很大。我使用了它,并在以下情况下遇到了无限循环,所以我想我会做出贡献。

开始时间:1:30 结束时间:2:00

开始时间:2:45 结束时间:4:00

开始时间:1:45 结束时间:3:00 (与前两个范围重叠)

所以这是我想出的浓缩版:

private static IEnumerable<TimeRange> MergeTimeRanges(IEnumerable<TimeRange> ranges)
{
    var mergedRanges = new List<TimeRange>();

    foreach (var range in ranges)
    {
        var overlapping = mergedRanges.Where(r => !(range.End < r.Start) && !(range.Start> r.End)).ToArray();
        if (overlapping.Length == 0)
        {
            mergedRanges.Add(range);
        }
        else
        {
            // add a new range made up of the overlapping ranges plus the new range, then delete the ovelapping ranges
            mergedRanges.Add(new TimeRange { Start = Math.Min(range.Start, overlapping.Min(r => r.Start)), End = Math.Max(range.End, overlapping.Max(r => r.End)) });
            foreach (var r in overlapping)
                mergedRanges.Remove(r);
        }
    }

    return mergedRanges;
}

注意:再想一想,可以通过对初始范围 (?) 进行排序来避免无限循环问题。没有把握。

于 2017-07-19T14:21:20.537 回答