Javascript - 向前移动重叠间隔

Javascript - Move forward overlapping intervals

有这样的输入:

[
 {key: 'a', start: 0, end: 100}, // 100ms of duration
 {key: 'b', start: 10, end: 30}, // 20ms of duration
 {key: 'c', start: 110, end: 200}, // 90ms of duration,
 {key: 'd', start: 300, end: 400},
]

我想要这个输出:

 {key: 'a', start: 0, end: 100}, // 100ms of duration
 {key: 'b', start: 100, end: 120}, // still 20ms of duration
 {key: 'c', start: 120, end: 210}, // still 90ms of duration
 {key: 'd', start: 300, end: 400}, // no modifications

我只需要修复重叠的间隔

获取项目的持续时间(通过从其 end 属性 中减去其 start 属性),设置其 start 属性 到最后一项的 end 属性 并将其 end 属性 设置为持续时间加上其 start 属性.

const arr=[{key:"a",start:0,end:100},{key:"b",start:10,end:30},{key:"c",start:110,end:200},{key:"d",start:300,end:400}];

for (let i = 1; i < arr.length; i++) {
  if (arr[i].start < arr[i - 1].end) {
    const duration = arr[i].end - arr[i].start;
    arr[i].start = arr[i - 1].end;
    arr[i].end = arr[i].start + duration;
  }
}

console.log(arr)

假设您的输出是一个对象数组,您可以使用 Array.reduce().

const input = [
  { key: 'a', start: 0, end: 100 }, // 100ms of duration
  { key: 'b', start: 10, end: 30 }, // 20ms of duration
  { key: 'c', start: 110, end: 200 }, // 90ms of duration
  { key: 'd', start: 300, end: 400 },
];

const output = input.reduce(
  /**
   * @param outArray {Array<{ key: string, start: number, end: number }>} The accumulated array output by .reduce()
   * @param key {string} The key of the current item
   * @param start {number} The start time of the current item
   * @param end {number} The end time of the current item
   * @param idx {number} The current item's index
   */
  (outArray, { key, start, end }, idx) => {
    // nextStart =
    //   if we're on the first item, use 0
    //   if start is greater than the end time, just use start
    //   else the end time of the previous item
    const nextStart = Math.max(start, outArray[idx - 1]?.end ?? 0);

    // add the new item to the output
    outArray.push({
      key,
      start: nextStart,
      // add the previous end (aka, the current start) and the duration of the item
      end: nextStart + end - start,
    });
    
    // return our output array
    return outArray;
  }, 
  [] // initialize our output array as the empty array
);

console.log(output);

如果您想使用经验丰富的 JS 程序员可能使用的可读性较差的 hax0r 语法,您可以使用一行 reduce() 回调来实现。

const input = [
  { key: 'a', start: 0, end: 100 }, // 100ms of duration
  { key: 'b', start: 10, end: 30 }, // 20ms of duration
  { key: 'c', start: 110, end: 200 }, // 90ms of duration
];

const output = input.reduce(
  // can be one line, but expanded for readability
  (outArray, { key, start, end }, idx) => [
    ...outArray, 
    { 
      key, 
      start: Math.max(start, outArray[idx - 1]?.end ?? 0), 
      end: Math.max(start, outArray[idx - 1]?.end ?? 0) + end - start 
    }
  ],
  []
);

console.log(output);

第一种方法 运行s 稍微 对于长输入数组(我测试了 1200 个条目)更快,因为第二种方法在每次迭代时创建一个新数组并重新计算outArray[idx - 1]?.end ?? 0。这段代码在我用了 4 年的机器上 运行 大约需要 16 秒。

const input = new Array(300).fill(0).flatMap((_, i) => {
  const keyBase = i * 4;
  i *= 400;

  return [
    { key: (keyBase).toString(36), start: i + 0, end: i + 100 }, // 100ms of duration
    { key: (keyBase + 1).toString(36), start: i + 10, end: i + 30 }, // 20ms of duration
    { key: (keyBase + 2).toString(36), start: i + 110, end: i + 200 }, // 90ms of duration
    { key: (keyBase + 3).toString(36), start: i + 300, end: i + 400 },
  ];
});

const method1 = () => input.reduce(
  (outArray, { key, start, end }, idx) => {
    const nextStart = Math.max(start, outArray[idx - 1]?.end ?? 0);
    
    outArray.push({
      key,
      start: nextStart,
      end: nextStart + end - start,
    });

    return outArray;
  },
  []
);

const method2 = () => input.reduce(
  (outArray, { key, start, end }, idx) => [
    ...outArray,
    {
      key,
      start: Math.max(start, outArray[idx - 1]?.end ?? 0),
      end: Math.max(start, outArray[idx - 1]?.end ?? 0) + end - start
    }
  ],
  []
);

const sum = (acc, x) => acc + x;
const avg = arr => arr.reduce(sum, 0) / arr.length;

const methods = ['Method1', 'Method2'];

const obs = new PerformanceObserver(items =>
  ['Method1', 'Method2'].forEach(m =>
    console.log(m, avg(items.getEntriesByName(m).map(e => e.duration)))
  ));

obs.observe({ type: 'measure', buffered: true });

for (let i = 0; i < 20000; i++) {
  methods.forEach((m, i) => {
    performance.mark(i);
    i === 0 ? method1() : method2();
    i === 0 ? performance.measure(m, i) : performance.measure(m, 0, i);
  });
}

setTimeout(() => obs.disconnect(), 0);