1

I recieve as input a list of date ranges. Some of them are overlapping, some are adjacent. Is there any collection that would accumulate the incoming date ranges?

E.g.:

(2018/01/01, 2018/01/02),
(2018/02/15, 2018/03/21),
(2018/01/10, 2018/01/10), 
(2018/01/03, 2018/01/09)

Result:

(2018/01/01, 2018/01/10), // 1st, 3rd, 4th lines combined
(2018/02/15, 2018/03/21)  // 2nd line
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
firelex
  • 129
  • 11
  • No, you'll have to do that yourself :( [Linq Aggregate](https://stackoverflow.com/questions/7105505/linq-aggregate-algorithm-explained) might help with the boilerplate loop code, but how (or whether) to merge, is something you'll need to implement. – nvoigt Sep 25 '18 at 13:19
  • where this date comes from `2018/02/21` its not in input? – er-sho Sep 25 '18 at 13:31

2 Answers2

3

No there are no such collections in the standard library. However, you can implement a custom simple method for it; providing that the period is Tuple<DateTime, DateTime>:

private static IEnumerable<Tuple<DateTime, DateTime>> Accumulate(
  IEnumerable<Tuple<DateTime, DateTime>> source) {

  var data = source
    .OrderBy(date => date.Item1)
    .ThenByDescending(date => date.Item2);

  DateTime left = DateTime.MinValue;  // make compiler be happy: initialization
  DateTime right = DateTime.MinValue; // -/-

  bool first = true;

  foreach (var item in data) {
    if (first) {
      left = item.Item1;
      right = item.Item2;

      first = false;
    }
    else if (right.AddDays(1) >= item.Item1) // can be combined; keep on combining
      right = item.Item2 > right ? item.Item2 : right;
    else {
      // can't be combined: return previous chunk
      yield return Tuple.Create(left, right);

      // start a new chunk
      left = item.Item1;
      right = item.Item2;
    }
  }

  // if we have a very last chunk to return, do it
  if (!first)
    yield return Tuple.Create(left, right);
}

Then

  Tuple<DateTime, DateTime>[] test = new Tuple<DateTime, DateTime>[] {
    Tuple.Create(new DateTime(2018, 01, 01), new DateTime(2018, 01, 02)),

    Tuple.Create(new DateTime(2018, 02, 15), new DateTime(2018, 03, 21)),
    Tuple.Create(new DateTime(2018, 01, 10), new DateTime(2018, 01, 10)),
    Tuple.Create(new DateTime(2018, 01, 03), new DateTime(2018, 01, 09)),
  };

  var result = Accumulate(test)
    .ToList();

  string report = string.Join("," + Environment.NewLine, result
    .Select(item => $"({item.Item1:yyyy'/'MM'/'dd}, {item.Item2:yyyy'/'MM'/'dd})"));

  Console.Write(report);

Outcome

(2018/01/01, 2018/01/10),
(2018/02/15, 2018/03/21)
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
0

I dont think there is a function or container in c# that will do this automaticly for you but of course you could extend some kind of collection to achieve this. For example take a simple List and only override the Add() method to extend a existing item by a range instead of adding a new one (if the rules you want to have for accumulating the date range apply)

Andre P
  • 123
  • 1
  • 8