在2个列表中查找重叠日期并返回重叠期

时间:2016-08-22 19:02:01

标签: c# .net linq

我有2个包含UTC日期的列表。要确定列表是否包含重叠日期,我将执行以下操作:

list1.Where(
    x =>
    list2.Any(
    y =>
    x.StartDate < y.EndDate &&
    y.StartDate < x.EndDate));

有没有办法实际返回重叠期?这些列表本身是唯一的,因为list1不包含重叠日期,list2将不包含iteself中的重叠日期。

例如,如果我有2个列表包含以下开始和结束日期时间

list 1:
   1/1 5AM - 1/1 10PM
   1/2 4AM - 1/2 8AM
list 2:
   1/1 10AM - 1/1 11AM
   1/1 4PM - 1/1 5PM
   1/2 5AM - 10PM

我想回复:

1/1 10AM - 1/1 11AM
1/1 4PM - 1/1 5Pm
1/2 5AM - 1/2 8AM

日期永远不会为NULL。

我正在考虑使用2次开始的最大值并且2次结束的MIN会起作用,但不确定这在语法上会是什么样的

4 个答案:

答案 0 :(得分:3)

给出以下DateRange类:

public class DateRange
{
    public DateRange(DateTime startDate, DateTime endDate)
    {
        StartDate = startDate;
        EndDate = endDate;
    }

    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

以及以下的非常DateTime比较函数:

public static DateTime MinDate(DateTime first, DateTime second)
{
    return first < second ? first : second;
}

public static DateTime MaxDate(DateTime first, DateTime second)
{
    return first > second ? first : second;
}

您可以使用以下Linq

list1.SelectMany(x =>
    list2.Where(y => x.StartDate < y.EndDate && y.StartDate < x.EndDate)
         .Select(y => new { first = x, second = y })))
             // Here you will have:
             // {
             //     x = (1/1 5AM - 1/1 10PM), y = (1/1 10AM - 1/1 11AM),
             //     x = (1/1 5AM - 1/1 10PM), y = (1/1 4PM - 1/1 5PM),
             //     x = (1/2 4AM - 1/2 8AM), y = (1/2 5AM - 10PM)
             // }
    .Select(x => new DateRange(MaxDate(x.first.StartDate, x.second.StartDate), MinDate(x.first.EndDate, x.second.EndDate))
             // Here you will have:
             // {
             //     (1/1 10AM - 1/1 11AM),
             //     (1/1 4PM - 1/1 5PM),
             //     (1/2 5AM - 1/2 8AM)
             // }

请注意,如果Linq Lists查询将是 O(n 2 这可以使用类似于sorted array merging的算法在 O(n) 中进行排序。

答案 1 :(得分:0)

这给了我你的例子的正确答案。

    class DateSpan
    {
        public DateTime StartDate;
        public DateTime EndDate;

        public DateSpan(DateTime start, DateTime end)
        {
            StartDate = start;
            EndDate = end;
        }

        public DateSpan(DateTime start, int duration)
        {
            StartDate = start;
            EndDate = start.AddHours(duration);
        }
    }

    public void TestStuff()
    {
        var dates1 = new System.Collections.Generic.List<DateSpan>();
        dates1.Add(new DateSpan(new DateTime(2016, 1, 1, 5, 0, 0), 17));
        dates1.Add(new DateSpan(new DateTime(2016, 1, 2, 4, 0, 0), 4));

        var dates2 = new System.Collections.Generic.List<DateSpan>();
        dates2.Add(new DateSpan(new DateTime(2016, 1, 1, 10, 0, 0), 1));
        dates2.Add(new DateSpan(new DateTime(2016, 1, 1, 16, 0, 0), 1 ));
        dates2.Add(new DateSpan(new DateTime(2016, 1, 2, 5,0,0), 17 ));

        var e = dates1.SelectMany((DateSpan x) =>
        {
            var result = new List<DateSpan>();
            foreach (var o in dates2.Where(y => x.StartDate < y.StartDate && y.StartDate < x.EndDate).ToList())
            {
                result.Add(new DateSpan(new DateTime(Math.Max(x.StartDate.Ticks, o.StartDate.Ticks)), new DateTime(Math.Min(x.EndDate.Ticks, o.EndDate.Ticks))));
            }
            return result;
        });
        //var d = dates1.Where(x => dates2.Any(y => x.StartDate < y.StartDate && y.StartDate < x.EndDate)).ToList();
    }

答案 2 :(得分:0)

以下是基于您的逻辑构建的代码。我没有使用whereany组合,而是使用笛卡尔积,它更清晰地代表了您的逻辑。只需合并MinMax功能即可获得您所要求的内容

struct DateRange {
    public DateTime StartDate, EndDate;
    public override string ToString() {
        return $"{StartDate} - {EndDate}";
    }
};

static void Main(string[] args) {
    var list1 = new List<DateRange>() {
        new DateRange() { StartDate = DateTime.Parse("1/1/2016 5AM"), EndDate = DateTime.Parse("1/1/2016 10PM") },
        new DateRange() { StartDate = DateTime.Parse("1/2/2016 4AM"), EndDate = DateTime.Parse("1/2/2016 8AM") }
    };
    var list2 = new List<DateRange>() {
        new DateRange() { StartDate = DateTime.Parse("1/1/2016 10AM"), EndDate = DateTime.Parse("1/1/2016 11AM")},
        new DateRange() { StartDate = DateTime.Parse("1/1/2016 4PM"), EndDate = DateTime.Parse("1/1/2016 5PM")},
        new DateRange() { StartDate = DateTime.Parse("1/2/2016 5AM"), EndDate = DateTime.Parse("1/2/2016 10PM")}
    };

    var overlapList = from outer in list1
                        from inner in list2
                        where outer.StartDate < inner.EndDate && inner.StartDate < outer.EndDate
                        select new DateRange() {
                            StartDate = new DateTime(Math.Max(outer.StartDate.Ticks, inner.StartDate.Ticks)),
                            EndDate = new DateTime(Math.Min(outer.EndDate.Ticks, inner.EndDate.Ticks))
                        };


}

答案 3 :(得分:0)

我使用let子句来使代码更具可读性:

var q = 
    from x in list1
    from y in list2
    let start = Max(x.StartDate, y.StartDate)
    let end = Min(x.EndDate, y.EndDate)
    where start < end
    select new { start, end };
foreach (var item in q)
{
    Console.WriteLine($"{item.start}-{item.end}");
}

MaxMin方法来自this answer