我目前正在开发一个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
答案 0 :(得分:8)
答案 1 :(得分:4)
这很有趣,所以我开始编码。
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();
}
}
}
如果有人有兴趣,我会对它进行一些测试
答案 2 :(得分:2)
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;
}
注意:第二个想法是,通过排序初始范围(?)可能避免了无限循环问题。不确定。