从一个列表中找到另一个列表中的剩余时间

时间:2019-03-12 14:05:15

标签: c# list datetime

我已经用这个把我的头撞在砖墙上了一段时间了! 我已经接近了,但我无法完全达到预期的效果,希望有人能够告诉我我要去哪里。

我有2个对象列表,其中包含开始时间和结束时间。

列表A

12/03/2019 04:13:50 - 12/03/2019 06:28:52
12/03/2019 06:31:06 - 12/03/2019 06:32:09
12/03/2019 06:33:11 - 12/03/2019 06:34:48
12/03/2019 06:35:26 - 12/03/2019 06:39:52
12/03/2019 06:42:33 - 12/03/2019 08:19:31
12/03/2019 08:30:03 - 12/03/2019 08:31:07
12/03/2019 08:36:56 - 12/03/2019 09:16:31
12/03/2019 09:17:17 - 12/03/2019 10:00:00

列表B

12/03/2019 06:25:35 - 12/03/2019 06:28:52
12/03/2019 06:45:23 - 12/03/2019 06:52:29
12/03/2019 06:57:43 - 12/03/2019 06:58:05
12/03/2019 06:59:46 - 12/03/2019 07:07:58
12/03/2019 07:11:09 - 12/03/2019 07:21:36
12/03/2019 07:33:10 - 12/03/2019 07:38:13
12/03/2019 07:39:54 - 12/03/2019 07:43:27
12/03/2019 07:44:01 - 12/03/2019 07:45:41
12/03/2019 07:49:59 - 12/03/2019 08:02:13
12/03/2019 08:03:31 - 12/03/2019 08:12:51
12/03/2019 08:17:09 - 12/03/2019 08:19:31
12/03/2019 08:42:04 - 12/03/2019 08:47:13
12/03/2019 09:51:37 - 12/03/2019 10:00:00 

我想从列表a中生成不在列表B中的开始和结束时间对象的新列表。

所需结果

12/03/2019 04:13:50 - 12/03/2019 06:25:35
12/03/2019 06:31:06 - 12/03/2019 06:32:09
12/03/2019 06:33:11 - 12/03/2019 06:34:48
12/03/2019 06:35:26 - 12/03/2019 06:39:52
12/03/2019 06:42:33 - 12/03/2019 06:45:23
12/03/2019 06:52:29 - 12/03/2019 06:57:43
12/03/2019 06:58:05 - 12/03/2019 06:59:46
12/03/2019 07:07:58 - 12/03/2019 07:11:09
12/03/2019 07:21:36 - 12/03/2019 07:33:10
12/03/2019 07:38:13 - 12/03/2019 07:39:54
12/03/2019 07:43:27 - 12/03/2019 07:44:01
12/03/2019 07:45:41 - 12/03/2019 07:49:59
12/03/2019 08:02:13 - 12/03/2019 08:03:31
12/03/2019 08:12:51 - 12/03/2019 08:17:09
12/03/2019 08:30:03 - 12/03/2019 08:31:07
12/03/2019 08:36:56 - 12/03/2019 08:42:04
12/03/2019 08:47:13 - 12/03/2019 09:16:31
12/03/2019 09:17:17 - 12/03/2019 09:51:37

这是我目前正在使我最接近的方法。

    public ShiftPattern GetRemainingTimesWithinShiftPattern(List<State> listB)
    {
        var shifts = new ShiftPattern();
        for (var a = 0; a < listB.Count; a++)
        {
            foreach (var shift in this) // this = listA
            {
                if (shift.IsTimeWithinShift(listB[a].StateStart) || shift.IsTimeWithinShift(listB[a].StateEnd))
                {
                    if (shift.IsTimeWithinShift(listB[a].StateStart))
                    {
                        var inoutShift = new Shift
                        {
                            StartDay = shift.StartDay,
                            StartTime = shift.StartTime,
                            EndDay = listB[a].StateStart.Date,
                            EndTime = listB[a].StartMsSinceMidnight
                        };
                        shifts.Add(inoutShift);
                    }
                    if (shift.IsTimeWithinShift(listB[a].StateEnd))
                    {
                        for (var c = a + 1; c < listB.Count; c++)
                        {
                            var inoutShift = new Shift();
                            inoutShift.StartDay = listB[a].StateEnd.Date;
                            inoutShift.StartTime = listB[a].EndMsSinceMidnight;
                            if (shift.IsTimeWithinShift(listB[c].StateStart))
                            {
                                inoutShift.EndDay = listB[c].StateStart.Date;
                                inoutShift.EndTime = listB[c].StartMsSinceMidnight;
                                shifts.Add(inoutShift);
                            }
                            else if (shift.EndTime > listB[a].EndMsSinceMidnight) // this is so we don't get a start and stop for the same time.
                            {
                                inoutShift.EndDay = shift.EndDay;
                                inoutShift.EndTime = shift.EndTime;
                                shifts.Add(inoutShift);
                                break;
                            }
                            a++;
                        }
                    }

                }
            }
        }
        return shifts;
    }

我得到的结果是

12/03/2019 04:13:50 - 12/03/2019 06:25:35
12/03/2019 06:42:33 - 12/03/2019 06:45:23
12/03/2019 06:52:29 - 12/03/2019 06:57:43
12/03/2019 06:58:05 - 12/03/2019 06:59:46
12/03/2019 07:07:58 - 12/03/2019 07:11:09
12/03/2019 07:21:36 - 12/03/2019 07:33:10
12/03/2019 07:38:13 - 12/03/2019 07:39:54
12/03/2019 07:43:27 - 12/03/2019 07:44:01
12/03/2019 07:45:41 - 12/03/2019 07:49:59
12/03/2019 08:02:13 - 12/03/2019 08:03:31
12/03/2019 08:12:51 - 12/03/2019 08:17:09
12/03/2019 09:17:17 - 12/03/2019 09:51:37

编辑

以下是几个产生列表A和B的示例类

    public class StartStop
    {
        public DateTime Start { get; set; }
        public DateTime Stop { get; set; }
    }
    public List<StartStop> GetListA()
    {
        return new List<StartStop>
        {
            new StartStop { Start = DateTime.Today.AddHours(4).AddMinutes(13).AddSeconds(50), Stop  = DateTime.Today.AddHours(6).AddMinutes(28).AddSeconds(52) },
            new StartStop { Start = DateTime.Today.AddHours(6).AddMinutes(31).AddSeconds(6), Stop  = DateTime.Today.AddHours(6).AddMinutes(32).AddSeconds(9) },
            new StartStop { Start = DateTime.Today.AddHours(6).AddMinutes(33).AddSeconds(11), Stop  = DateTime.Today.AddHours(6).AddMinutes(34).AddSeconds(48) },
            new StartStop { Start = DateTime.Today.AddHours(6).AddMinutes(35).AddSeconds(26), Stop  = DateTime.Today.AddHours(6).AddMinutes(39).AddSeconds(52) },
            new StartStop { Start = DateTime.Today.AddHours(6).AddMinutes(42).AddSeconds(33), Stop  = DateTime.Today.AddHours(8).AddMinutes(19).AddSeconds(31) },
            new StartStop { Start = DateTime.Today.AddHours(8).AddMinutes(30).AddSeconds(3), Stop  = DateTime.Today.AddHours(8).AddMinutes(31).AddSeconds(7) },
            new StartStop { Start = DateTime.Today.AddHours(8).AddMinutes(36).AddSeconds(56), Stop  = DateTime.Today.AddHours(9).AddMinutes(16).AddSeconds(31) },
            new StartStop { Start = DateTime.Today.AddHours(9).AddMinutes(17).AddSeconds(17), Stop  = DateTime.Today.AddHours(10) }
        };
    }
    public List<StartStop> GetListB()
    {
        return new List<StartStop>
        {
            new StartStop { Start = DateTime.Today.AddHours(6).AddMinutes(25).AddSeconds(35), Stop  = DateTime.Today.AddHours(6).AddMinutes(28).AddSeconds(52) },
            new StartStop { Start = DateTime.Today.AddHours(6).AddMinutes(45).AddSeconds(23), Stop  = DateTime.Today.AddHours(6).AddMinutes(52).AddSeconds(29) },
            new StartStop { Start = DateTime.Today.AddHours(6).AddMinutes(57).AddSeconds(43), Stop  = DateTime.Today.AddHours(6).AddMinutes(58).AddSeconds(5) },
            new StartStop { Start = DateTime.Today.AddHours(6).AddMinutes(59).AddSeconds(46), Stop  = DateTime.Today.AddHours(7).AddMinutes(7).AddSeconds(58) },
            new StartStop { Start = DateTime.Today.AddHours(7).AddMinutes(11).AddSeconds(9), Stop  = DateTime.Today.AddHours(7).AddMinutes(21).AddSeconds(36) },
            new StartStop { Start = DateTime.Today.AddHours(7).AddMinutes(33).AddSeconds(10), Stop  = DateTime.Today.AddHours(7).AddMinutes(38).AddSeconds(13) },
            new StartStop { Start = DateTime.Today.AddHours(7).AddMinutes(39).AddSeconds(54), Stop  = DateTime.Today.AddHours(7).AddMinutes(43).AddSeconds(27) },
            new StartStop { Start = DateTime.Today.AddHours(7).AddMinutes(44).AddSeconds(1), Stop  = DateTime.Today.AddHours(7).AddMinutes(45).AddSeconds(41) },
            new StartStop { Start = DateTime.Today.AddHours(7).AddMinutes(49).AddSeconds(59), Stop  = DateTime.Today.AddHours(8).AddMinutes(2).AddSeconds(13) },
            new StartStop { Start = DateTime.Today.AddHours(8).AddMinutes(3).AddSeconds(31), Stop  = DateTime.Today.AddHours(8).AddMinutes(12).AddSeconds(51) },
            new StartStop { Start = DateTime.Today.AddHours(8).AddMinutes(17).AddSeconds(9), Stop  = DateTime.Today.AddHours(8).AddMinutes(19).AddSeconds(31) },
            new StartStop { Start = DateTime.Today.AddHours(8).AddMinutes(42).AddSeconds(4), Stop  = DateTime.Today.AddHours(8).AddMinutes(47).AddSeconds(13) },
            new StartStop { Start = DateTime.Today.AddHours(9).AddMinutes(51).AddSeconds(37), Stop  = DateTime.Today.AddHours(10) },
        };
    }

1 个答案:

答案 0 :(得分:1)

我将使用LINQ将其分解为类似的内容。未经测试,因为我不需要您的示例数据中的打字练习 1

  var listA = new List<Something>();
  var listB = new List<Something>();
  var result = new List<Something>();

  var allPeriods = listA
    .SelectMany(st => new[] {
      new { AtTime = st.StartTime, A = 1, B = 0 },
      new { AtTime = st.EndTime, A = -1, B = 0} })
    .Concat(listB.SelectMany(st => new[]
    {
      new {AtTime = st.StartTime, A = 0, B = 1},
      new {AtTime = st.EndTime, A = 0, B = -1}
    }));
  var sorted = allPeriods.OrderBy(per => per.AtTime).ToList();
  var paired = sorted.Zip(sorted.Skip(1),
       (first, second) => new { Start = first, End = second });
  var a = 0;
  var b = 0;
  foreach(var pair in paired)
  {
    a += pair.Start.A;
    b += pair.Start.B;
    if (a > 0 && b == 0)
    {
      result.Add(new Something {
        StartTime = pair.Start.AtTime,
        EndTime = pair.End.AtTime });
    }
  }

希望您可以在这里看到我正在使用的逻辑。我基本上会提取所有可能的开始和结束时间,并按顺序将它们配对。然后,我对它们进行处理,并持续计数现在打开了多少个A和B。如果存在“开放”但没有B,则输出结果。

这不会合并相邻的期间,但无论如何都不应该根据您的样本数据进行。

不幸的是,这种方法确实需要对数据进行两次传递。除非这是性能热点,并且您正在处理包含很多个项目的列表,否则我不必担心。

尽管至少有一个列表需要进行基于起始日期和结束日期的某种多维范围访问,但唯一有效的方法不会多次通过,我想不出什么标准适用于此的框架。


在某种程度上,这是基于我如何解决SQL中的相同问题,它可以更自然地适应这种基于集合的方法。其他人可能更喜欢声明式的方法。


1 如果我采用您的样本数据代码并进行了纠正以防止时间旅行,现在将产生18个结果,这些结果在现场检查时与您的预期结果相符。