我有一个事件列表,现在我想找出哪些事件重叠。您可以在下面找到我目前拥有的代码,但我遇到的问题是,搜索到的项目也包含在列表中。
List<SomeEventObject> overlappingEvents = new List<SomeEventObject>();
foreach (SomeEventObject eventItem in EventList)
{
bool overlapping = false;
foreach (SomeEventObject anotherEventItem in EventList)
{
if (eventItem.StartDate <= anotherEventItem.EndDate &&
eventItem.EndDate >= anotherEventItem.StartDate)
{
overlapping = true;
overlappingEvents.Add(anotherEventItem);
}
}
if (overlapping)
overlappingEvents.Add(eventItem);
}
我需要创建一个没有搜索项目的新列表。因此我问是否有一个很好的LINQ表达式可以为我处理。这是我想到的一些伪代码:
EventList.Where(e =>
eventItem.StartDate <= e.EndDate &&
eventItem.EndDate >= e.StartDate);
在这种情况下, eventItem 当然不存在。
结果我认为我需要两个列表:一个列表有重叠事件,一个列表有非重叠事件。但如果我有重叠的事件列表,那么.Except()
应该可以实现。
修改
我创建了一个dotnetfiddle,以便可以使用它。一个重要的问题是重叠算法。
活动1:
StartDate:今天,10:00
EndDate:今天,10:05
活动2:
StartDate:今天,10:05
EndDate:今天,10:10
如果您向用户提供此信息,则不会重叠。所以我要修改我的算法。
答案 0 :(得分:3)
我会这样做:
var overlappingEvents =
(
from e1 in EventList
from e2 in EventList
where e1 != e2
where e1.StartDate <= e2.EndDate
where e1.EndDate >= e2.StartDate
from e in new [] { e1, e2 }
select e
).ToList();
我认为这应该是非常直接的。
根据评论,此版本只返回一个EventList
元素,无论它参与多少重叠。
var overlappingEvents =
(
from e1 in EventList
where EventList
.Where(e2 => e1 != e2)
.Where(e2 => e1.StartDate <= e2.EndDate)
.Where(e2 => e1.EndDate >= e2.StartDate)
.Any()
select e1
).ToList();
它也可以写成:
var overlappingEvents =
EventList
.Where(e1 =>
EventList
.Where(e2 => e1 != e2)
.Where(e2 => e1.StartDate <= e2.EndDate)
.Where(e2 => e1.EndDate >= e2.StartDate)
.Any())
.ToList();
根据进一步的评论,以下是如何配对事件:
var overlappingEvents =
(
from e1 in EventList
from e2 in EventList
where e1 != e2
where e1.StartDate <= e2.EndDate
where e1.EndDate >= e2.StartDate
select new [] { e1, e2 }
).ToList();
现在overlappingEvents
是一个数组列表 - List<SomeEventObject[]>
而不是List<SomeEventObject>
。该数组包含重叠事件。
答案 1 :(得分:2)
我会尝试这样的事情:
var searchedFor = EventList.First(); // Replace by the proper one
var overlapping = EventList.Where(e => e != searchedFor &&
EventList.Any(ev => e != ev && ev.StartDate <= e.EndDate && ev.EndDate >= e.StartDate))
// Alternatively with Except
var overlapping = EventList.Except(new[] { searchFor }).Where(e =>
EventList.Any(ev => e != ev && ev.StartDate <= e.EndDate && ev.EndDate >= e.StartDate))
对于非重叠,您只需使用EventList
从Except
过滤重叠事件:
var nonOverlapping = EventList.Except(overlappingEvents);
我不知道您的SomeEventObject
,但您可能需要通过一些Id
比较来更改不等式比较。
您可能需要查看quite good Q&A on detecting overlapping periods in C#。为方便起见,您还可以将检查分解如下:
Func<SomeEventObject, SomeEventObject, bool> areOverlapped = (e1, e2) =>
e1.StartDate <= e2.EndDate && e1.EndDate >= e2.StartDate;
var overlapping = EventList.Where(e => e != searchedFor &&
EventList.Any(ev => e != ev && areOverlapped(e, ev)));
答案 2 :(得分:1)
你可以用这个得到结果:
var nonOverlappingEvents = EventList.Where(e1 => !EventList.Where(e2 => e2 != e1).Any(e2 => e1.StartDate <= e2.EndDate && e1.EndDate >= e2.StartDate));
但我不明白你的重叠算法。我认为重叠意味着一个事件的StartDate
介于另一个事件的StartDate
和EndDate
之间:
var nonOverlappingEvents = EventList.Where(e1 => !EventList.Where(e2 => e2 != e1).Any(e2 => e2.StartDate >= e1.StartDate && e2.StartDate <= e1.EndDate));
答案 3 :(得分:1)
我知道你说你想要使用Linq,它肯定是相当直接的。我们曾经这样做 - 但是我们做了很多日期范围工作,并找到了更好的方法。
如果你正在做很多这段时间的工作,我会推荐Itenso Time Period Library(可在Nuget上找到)。
它支持非常丰富的基于时间的操作。例如IsSamePeriod,HasInside,OverlapsWith或IntersectsWith可用于期间关系。
它还支持时间收集,例如一个人的所有活动。这些基于ITimePeriodCollection
可以保存ITimePeriod
类型的任意元素,并将其所有元素的最早开始解释为收集时间段的开始。相应地,所有元素的最新结束都是收集期的结束。
一些代码示例,了解它的工作原理
//Setup a time range
TimeRange timeRange1 = new TimeRange(
new DateTime( 2011, 2, 22, 14, 0, 0 ),
new DateTime( 2011, 2, 22, 18, 0, 0 ) );
Console.WriteLine( "TimeRange1: " + timeRange1 );
// > TimeRange1: 22.02.2011 14:00:00 - 18:00:00 | 04:00:00
// --- Setu uptime range 2 ---
TimeRange timeRange2 = new TimeRange(
new DateTime( 2011, 2, 22, 15, 0, 0 ),
new TimeSpan( 2, 0, 0 ) );
Console.WriteLine( "TimeRange2: " + timeRange2 );
// > TimeRange2: 22.02.2011 15:00:00 - 17:00:00 | 02:00:00
// --- relation ---
Console.WriteLine( "TimeRange1.GetRelation( TimeRange2 ): " +
timeRange1.GetRelation( timeRange2 ) );
// --- intersection ---
Console.WriteLine( "TimeRange1.GetIntersection( TimeRange2 ): " +
timeRange1.GetIntersection( timeRange2 ) );
// > TimeRange1.GetIntersection( TimeRange2 ):
// 22.02.2011 15:00:00 - 17:00:00 | 02:00:00
答案 4 :(得分:0)
不使用linq,而是将重叠的时间段归为一组:
public class Range
{
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
public Range(DateTime start, DateTime stop)
{
Start = start;
Stop = stop;
}
}
void Main()
{
List<Range> ranges = new List<Range>();
ranges.Add(new Range(new DateTime(2019, 10, 1), new DateTime(2019, 10, 2)));
ranges.Add(new Range(new DateTime(2019, 10, 2), new DateTime(2019, 10, 3)));
ranges.Add(new Range(new DateTime(2019, 10, 1), new DateTime(2019, 10, 3)));
ranges.Add(new Range(new DateTime(2019, 10, 4), new DateTime(2019, 10, 5)));
ranges.Add(new Range(new DateTime(2019, 10, 6), new DateTime(2019, 10, 8)));
ranges.Add(new Range(new DateTime(2019, 10, 5), new DateTime(2019, 10, 7)));
ranges.Add(new Range(new DateTime(2019, 10, 9), new DateTime(2019, 10, 9)));
var rangesOrdered = ranges.OrderBy(t => t.Start).ToList();
var sets = new List<List<Range>>() { new List<Range>() {rangesOrdered[0]}};
var IsOverlaping = new Func<Range, Range, bool>((a, b) => a.Start < b.Stop && b.Start < a.Stop);
for (var i = 1; i < rangesOrdered.Count; i++)
{
if (IsOverlaping(sets.Last().OrderBy(s => s.Stop).Last(), rangesOrdered[i]))
{
sets.Last().Add(rangesOrdered[i]);
}
else
{
sets.Add(new List<Range>() {rangesOrdered[i]});
}
}
}