Поиск оставшегося времени из одного списка, которого нет в другом - PullRequest
1 голос
/ 12 марта 2019

Я бился головой об кирпичную стену некоторое время с этим!Я близок, но не могу получить желаемый результат и надеялся, что кто-то может сказать мне, где я ошибаюсь.

У меня есть 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 Ответ

1 голос
/ 12 марта 2019

Я бы сломал что-то вроде этого, используя 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 });
    }
  }

Надеюсь, вы можете увидеть логику, которую я здесь использую.Я в основном извлекаю все возможные времена начала и окончания и объединяю их в порядке.Затем я обрабатываю их и продолжаю подсчитывать, сколько А и В теперь открыто.Если есть Открыто как, но нет B, мы выводим результат.

Это не объединит смежные периоды, но они все равно не должны возникать на основе ваших выборочных данных.

К сожалению, этот подход делаетнужно сделать пару проходов по данным.Если только это не точка производительности, и вы имеете дело со списками, содержащими лотов элементов, я бы об этом не беспокоился.

Единственные эффективные подходы, которые ненесколько проходов, хотя, по крайней мере, для одного из списков требуется какой-то многомерный доступ к диапазону, основанный как на датах начала, так и на конце, и я не могу придумать ничего стандартного в структуре, который бы работал для этого.


В некоторой степени это основано на том, как бы я решил ту же проблему в SQL, который может более естественно ориентироваться на такого рода подход на основе множеств.Другие могут предпочесть более декларативные подходы.


1 Если я возьму ваш пример кода данных и внесу исправления для предотвращения перемещения во времени, то теперь это даст 18 результатов, которые при выборочной проверке соответствуют вашиможидаемые результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...