简体   繁体   English

合并重叠的日期范围

[英]Merge overlapping date ranges

How can I merge overlapping date ranges, on an array?如何在数组上合并重叠的日期范围?

I have an array of dates:我有一系列日期:

const dates = [
  {startDate: "2020-03-19T00:00:00+01:00", endDate: "2020-03-20T00:00:00+01:00"},
  {startDate: "2020-03-09T00:00:00+01:00", endDate: "2020-03-16T00:00:00+01:00"},
  {startDate: "2020-02-07T00:00:00+01:00", endDate: "2020-03-09T00:00:00+01:00"},
  {startDate: "2020-02-07T00:00:00+01:00", endDate: "2020-02-13T00:00:00+01:00"}
];

What I'm looking to accomplish is to have the overlapping arrays merged so I would have as result:我想要完成的是合并重叠数组,这样我就会得到结果:

//Result I'm looking for:
const mergedDates = [
  {startDate: "2020-03-19T00:00:00+01:00", endDate: "2020-03-20T00:00:00+01:00"},
  {startDate: "2020-02-07T00:00:00+01:00", endDate: "2020-03-16T00:00:00+01:00"}
];

I'm using the moment-range to create the ranges:我正在使用moment-range创建范围:

const ranges = dates.map(d => {
  return moment.range(d.startDate, d.endDate);
});

Then I'm using two for loops to find out the overlapping然后我使用两个for循环来找出重叠

  let arrRange = [];

  for (let i = 0; i < ranges.length; i++) {
    const el1 = ranges[i];
    let loop=[];
    for (let i = 0; i < ranges.length; i++) {
      const el2 = ranges[i];
      const overlaps = el1.overlaps(el2, { adjacent: true });
      if(overlaps){
        loop = [...loop, i]
      }
    }
    arrRange.push(loop);
  }
}

This gives me an array where I have arrays of indexes, so I know where are the overlaps:这给了我一个数组,其中有索引数组,所以我知道重叠在哪里:

console.log(arrRange);
// [[0], [1, 2], [1, 2, 3], [2, 3]]

However, I'm stuck.但是,我被困住了。

Even knowing the overlaps, I can't figure out how to merge them.即使知道重叠,我也无法弄清楚如何合并它们。

I'd use Math.min and Math.max to get the first and last date endpoints for each overlapping range, then reduce each index array to a merged range object:我会使用Math.minMath.max来获取每个重叠范围的第一个和最后一个日期端点,然后将每个索引数组减少到合并的范围对象:

const merged = arrRange.map(idxArray => {
  const { min, max } = idxArray.reduce(({ min, max }, index) => {
    const range = ranges[index];
    return {
      min: Math.min(min, range.start.toDate()),
      max: Math.max(max, range.end.toDate())
    };
  }, { min: Number.MAX_VALUE, max: Number.MIN_VALUE }); // throwaway value
  return moment.range(new Date(min), new Date(max));
});
        var rawArray = [
        { name: 'C', startDate: '2021/02/01', endDate: '2021/02/15' },
        { name: 'A', startDate: '2021/01/01', endDate: '2021/01/15' },
        { name: 'D', startDate: '2021/02/12', endDate: '2021/02/25' },
        { name: 'E', startDate: '2021/02/22', endDate: '2021/02/28' },
        { name: 'F', startDate: '2021/03/01', endDate: '2021/03/15' },
        { name: 'B', startDate: '2021/01/12', endDate: '2021/01/30' },
    ]
    console.log('Input', rawArray)
    
        for (var i = 0; i < rawArray.length; i++) {
            for (var j = i + 1; j < rawArray.length; j++) {
                if (isBetweenOrOverlap(rawArray[i].startDate, rawArray[i].endDate, rawArray[j].startDate, rawArray[j].endDate)) {
                    rawArray[i].startDate = getLeastDate(rawArray[i].startDate, rawArray[j].startDate);
                    rawArray[i].endDate = getMaxDate(rawArray[i].endDate, rawArray[j].endDate);
                    rawArray.splice(j, 1);
                    i = i - 1;
                    break;
                }
            }
        }
    
        console.log('Ouput', rawArray)

Input [
  { name: 'C', startDate: '2021/02/01', endDate: '2021/02/15' },
  { name: 'A', startDate: '2021/01/01', endDate: '2021/01/15' },
  { name: 'D', startDate: '2021/02/12', endDate: '2021/02/25' },
  { name: 'E', startDate: '2021/02/22', endDate: '2021/02/28' },
  { name: 'F', startDate: '2021/03/01', endDate: '2021/03/15' },
  { name: 'B', startDate: '2021/01/12', endDate: '2021/01/30' }
]
Ouput [
  { name: 'C', startDate: '2021/02/01', endDate: '2021/02/28' },
  { name: 'A', startDate: '2021/01/01', endDate: '2021/01/30' },
  { name: 'F', startDate: '2021/03/01', endDate: '2021/03/15' }
]

I had to do this today (typescript, but you get the idea):我今天必须这样做(打字稿,但你明白了):

interface DateRange {
  start: Date;
  end: Date;
}
export function mergeOverlappingDateRanges(
  dateRanges: DateRange[],
): DateRange[] {
  const sorted = dateRanges.sort(
    // By start, ascending
    (a, b) => a.start.getTime() - b.start.getTime(),
  );

  const ret = sorted.reduce((acc, curr) => {
    // Skip the first range
    if (acc.length === 0) {
      return [curr];
    }

    const prev = acc.pop();

    if (curr.end <= prev.end) {
      // Current range is completely inside previous
      return [...acc, prev];
    }

    // Merges overlapping (<) and contiguous (==) ranges
    if (curr.start <= prev.end) {
      // Current range overlaps previous
      return [...acc, { start: prev.start, end: curr.end }];
    }

    // Ranges do not overlap
    return [...acc, prev, curr];
  }, [] as DateRange[]);

  return ret;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM