在其他间隔之间插入间隔的更好方法?

时间:2019-01-21 09:15:01

标签: javascript arrays

我从后端收到这样的数组:

[
  {index: 0, start: '07:00', end: '09:00'},
  {index: 1, start: '09:00', end: '11:00'},
  {index: 0, start: '12:00', end: '15:00'},
]

为了更好地使用它,我决定尝试将其转换为如下形式:

// this is array1
[
  {index: 0, time: startInMilliseconds},
  {index: 1, time: startInMilliseconds}, // no difference between 0's end and 1's start, no no null here
  {index: null, time: endInMilliseconds},
  {index: 0, time: startInMilliseconds},
  {index: null, time: endInMilliseconds}
]

因为我需要能够像这样插入和替换间隔:如果我用start: '08:00'end: '08:30'插入对象,则应该将第一个间隔分成两部分。如果start: '10:00'end: '12:30',则前一个间隔应该在10:00结束,下一个间隔应该在12:30开始,并且它们之间的空白时间段应该消失。

当前,在按时间对对象进行排序之后,我正在尝试使用for循环和许多if来做到这一点,就像这样:

intervals.push(
    {index: index, time: startInMilliseconds, new: true},
    {index: null, time: endInMilliseconds, new: true}
);

intervals.sort((a,b) => {
    if(a.time < b.time) return -1;
    if(a.time > b.time) return 1;
    if(!a.new && b.new) return -1;
    if(a.new && !b.new) return 1;
    return 0;
});

for(let i=0, iStart=null, lastIndex=null; i<intervals.length; i++){
    if(intervals[i].time == startInMilliseconds){
        if(intervals[i].new){
            if(intervals[i-1] && intervals[i-1].index == intervals[i].index){
                iStart = i-1;
                intervals.splice(i, 1);
                i--;
            }else{
                iStart = i;
                delete intervals[i].new;
            }
        }else{
            intervals.splice(i, 1);
            i--;
        }
        continue;
    }
    if(iStart != null && intervals[i].time <= endInMilliseconds && !intervals[i].new){
        lastIndex = intervals[i].index;
        intervals.splice(i, 1);
        i--;
        continue;
    }
    if(iStart != null && intervals[i].time == endInMilliseconds && intervals[i].new){
        if(lastIndex == intervals[iStart].index) intervals.splice(i, 1);
        else{
            intervals[i].index = lastIndex;
            delete intervals[i].new;
        }
        break;
    }
}

这显然很难遵循并且不能完全起作用,但是我想不出另一种方法。我也不知道该如何正确地向Google描述。

以下是一些示例,用于说明执行某些操作后array1的外观。假设字符串是以毫秒为单位的时间:

// insert {index: 2, start: '08:00', end: '08:30'}

[
  {index: 0, time: '07:00'},
  {index: 2, time: '08:00'},
  {index: 0, time: '08:30'},
  {index: 1, time: '09:00'},
  {index: null, time: '11:00'},
  {index: 0, time: '12:00'},
  {index: null, time: '15:00'}
]

// insert {index: 2, start: '10:00', end: '12:30'}

[
  {index: 0, time: '07:00'},
  {index: 1, time: '09:00'},
  {index: 2, time: '10:00'},
  {index: 0, time: '12:30'},
  {index: null, time: '15:00'}
]

// insert {index: 1, start: '10:00', end: '12:30'}

[
  {index: 0, time: '07:00'},
  {index: 1, time: '09:00'},
  {index: 0, time: '12:30'},
  {index: null, time: '15:00'}
]

1 个答案:

答案 0 :(得分:0)

我不得不重构您的代码,以下使用原始数组格式和间隔的顺序处理。希望评论足够。

我将时间减少到几分钟,您可以使用任何喜欢的常见因素,只需将 timeToMins 函数替换为任何合适的函数即可。它还删除了减少到零时间的间隔,即开始和结束是相同的。假定间隔已排序且有效,即值,开始<结束,范围不重叠(但插入的间隔可以与现有间隔重叠)等。

分割范围时,它使用Object.assign复制当前间隔,该间隔适用于简单对象,但是如果对象更复杂,则需要更好的东西。

// Convert time in HH:mm to minutes
function timeToMins(time) {
  let [h, m] = time.split(':');
  return h*60 + m*1;
}

function insertInterval(arr, nInterval) {
  var nStart = timeToMins(nInterval.start);
  var nEnd   = timeToMins(nInterval.end);
  var inserted = false;
  var temp;
  
  // Insert after current if after true, otherwise before
  function insert(after) {
    if (!inserted) {
      arr.splice(i + Number(!!after), 0, nInterval);
      inserted = true;
      i++;
    }
  }
  
  for (var i=0; i<arr.length; i++) {
     let cInterval = arr[i];
     let cStart = timeToMins(cInterval.start);
     let cEnd   = timeToMins(cInterval.end);
     
    // Cases where new start <= current start
    if (nStart <= cStart) {
      // If before first or between another range
      if (nEnd <= cStart) {
        insert();
        return;
      // If overlaps start only
      } else if (nEnd <= cEnd) {
        insert();
        cInterval.start = nInterval.end;
      // If overlaps end too, remove current
      } else if (nEnd >= cEnd) {
        insert();
        arr.splice(i, 1);
        --i;
      }
    // cases where new start > current start
    } else {  // if (nStart > cStart) {
      // If starts in the range
      if (nStart < cEnd) {
        insert(true);
        temp = cInterval.end; // Remember end in case it's needed below
        cInterval.end = nInterval.start;
      }
      // If also ends in the range, need to split range
      if (nEnd < cEnd) {
        insert(true);
        var tail = Object.assign({}, cInterval);
        tail.start = nInterval.end;
        tail.end = temp;
        arr.splice(++i, 0, tail);
      }
    }
    // If cInterval is now of zero length, remove it
    if (cInterval.start == cInterval.end) {
      arr.splice(i, 1);
      --i;
    }
  }
  // If didn't get inserted, must be after last
  if (!inserted) arr.push(nInterval);
}

// Examples
var data = [
  {index: 0, start: '07:00', end: '08:00'}, // 0
  {index: 1, start: '09:00', end: '10:00'}, // 1
  {index: 0, start: '10:00', end: '11:00'}  // 2
];

// Some interesting cases, there are many more to test...
[ {index: 1, start: '07:05', end:'07:15'},  // Splits first
  {index: 1, start: '08:05', end:'08:15'},  // Between 0 & 1  
  {index: 0, start: '09:30', end: '10:15'}  // Overlaps 1 end & 2 start
].forEach( interval => {
  insertInterval(data, interval);
  console.log(data.map(a=>JSON.stringify(a)).join('\n'));
});