如何根据给定数字增加值将数组拆分为多个数组?

How to split an array into a number of arrays based on increasing value by a given number?

我编写了以下函数:

const trends = hits.reduce((arr, curr, index, array) => {
      if (arr.includes(curr)) return arr
      if (curr + 1 === array[index + 1]) arr.push(curr, array[index + 1]);
      return arr;
    }, []);

要点是,如果一个数组包含一个递增 1 的数字序列,那么这个 return 就是一个具有这些值的新数组。 例如:[1, 2, 3, 6, 10] 会 return [1, 2, 3].

问题是:如果有多个序列,我想将它放在一个单独的数组中(或子数组的数组中)。此时,该函数执行以下操作 [1, 2, 3, 6, 7, 8]。我也无法预测可能会有多少种趋势。我怎样才能做到这一点?

您可以让累加器成为部分结果数组和先前值的组合(以便于访问它)。为了保持代码简单,您可以将每个当前值添加到最后一个子数组,或者作为它自己的子数组。 reduce完成后,可以踢出那些只有一个值的子数组:

let hits = [2, 4, 5, 6, 8, 10, 11, 12, 14];

const trends = hits.reduce(([arr, prev], curr, index, array) => {
    if (prev + 1 == curr) arr[arr.length-1].push(curr);
    else arr.push([curr]);
    return [arr, curr];
}, [[], NaN])[0].filter(row => row.length > 1);
    
console.log(trends);

一种直接的方法,基于优先级不能为 changed/swapped 的两个条件,它实际上也读取了它的作用...

function collectItemSequences(list, item, idx, arr) {
  if ((item - 1) === arr[idx - 1]) {
    // in case of a predecessor ...

    // ... push item into the most recent sequence list.
    list[list.length - 1].push(item);

  } else if ((item + 1) === arr[idx + 1]) {
    // else, in case of a successor ...

    // ... create a new sequence list with its 1st item.
    list.push([ item ]);
  }
  return list;
}

console.log(
  [2, 4, 6, 8, 10, 12, 14].reduce(collectItemSequences, [])
);
console.log(
  [2, 4, 5, 6, 8, 10, 11, 12, 14].reduce(collectItemSequences, [])
);
console.log(
  [1, 2, 4, 5, 6, 8, 10, 11, 12, 14, 15].reduce(collectItemSequences, [])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

基于上述方法,可以实现一种更通用的方法,它允许配置如何分别计算当前项目的序列前驱 序列后继 ...

function collectItemSequencesByConditions(collector, item, idx, arr) {
  const { getPredecessor, getSuccessor, list } = collector;
  if (getPredecessor(item) === arr[idx - 1]) {

    // push item into the most recent sequence list.
    list[list.length - 1].push(item);

  } else if (getSuccessor(item) === arr[idx + 1]) {

    // create a new sequence list with its 1st item.
    list.push([ item ]);
  }
  return collector;
}

const conditions = {
  getPredecessor: currentItem => currentItem - 2,
  getSuccessor: currentItem => currentItem + 2,
};

console.log(
  [2, 4, 6, 8, 10, 12, 14].reduce(
    collectItemSequencesByConditions,
    { ...conditions, list: [] },
  ).list
);
console.log(
  [2, 4, 5, 6, 8, 10, 11, 12, 14].reduce(
    collectItemSequencesByConditions,
    { ...conditions, list: [] },
  ).list
);
console.log(
  [1, 2, 4, 5, 6, 8, 10, 11, 12, 14, 15].reduce(
    collectItemSequencesByConditions,
    { ...conditions, list: [] },
  ).list
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

编辑

OP 的问题

I set up two pair of conditions, one for item - 1, item + 1, second pair respectively for - 10, + 10. The hits array was [22, 31, 32, 33, 42, 52]. I turned your console.logs into const variable = hits.reduce... so on. Then I returned both variables. The results were [31, 32, 33] and [42, 52]. The expected outcome for second is of course [22, 33, 42, 52].

首先,OP 很可能意味着 [22, 32, 42, 52]

其次...

不,数学是可靠的。而且算法不能被欺骗。适用于有效 predecessors/successors 的规则是无情的。因此 [22, 31, 32, 33, 42, 52]+/- 10 的预期结果是 [42, 52] 而不是 [22, 32, 42, 52].

为什么?.. [22, 31, 32, 33, 42, 52] 的第二个值是 31,它打破了任何可能的序列( OP 预期 2232)。因此它不是一个有效的 predecessor/successor 序列。

这里有一些测试用例...

console.log(
  "for [22, 31, 32, 33, 42, 52] and [-1 , +1]",
  "\nexpect: '[[31,32,33]]' ?",
  JSON.stringify([22, 31, 32, 33, 42, 52].reduce(
    collectItemSequencesByConditions, {
      getPredecessor: currentItem => currentItem - 1,
      getSuccessor: currentItem => currentItem + 1,
      list: [],
    }
  ).list) === '[[31,32,33]]'
);
console.log(
  [22, 31, 32, 33, 42, 52].reduce(
    collectItemSequencesByConditions, {
      getPredecessor: currentItem => currentItem - 1,
      getSuccessor: currentItem => currentItem + 1,
      list: [],
    }
  ).list
);

console.log(
  "for [22, 31, 32, 33, 42, 52] and [-10 , +10]",
  "\nexpect: '[[42,52]]' ?",
  JSON.stringify([22, 31, 32, 33, 42, 52].reduce(
    collectItemSequencesByConditions, {
      getPredecessor: currentItem => currentItem - 10,
      getSuccessor: currentItem => currentItem + 10,
      list: [],
    }
  ).list) === '[[42,52]]'
);
console.log(
  [22, 31, 32, 33, 42, 52].reduce(
    collectItemSequencesByConditions, {
      getPredecessor: currentItem => currentItem - 10,
      getSuccessor: currentItem => currentItem + 10,
      list: [],
    }
  ).list
);

console.log(
  "for [21, 22, 32, 33, 42, 52] and [-10 , +10]",
  "\nexpect: '[[22,32],[42,52]]' ?",
  JSON.stringify([21, 22, 32, 33, 42, 52].reduce(
    collectItemSequencesByConditions, {
      getPredecessor: currentItem => currentItem - 10,
      getSuccessor: currentItem => currentItem + 10,
      list: [],
    }
  ).list) === '[[22,32],[42,52]]'
);
console.log(
  [21, 22, 32, 33, 42, 52].reduce(
    collectItemSequencesByConditions, {
      getPredecessor: currentItem => currentItem - 10,
      getSuccessor: currentItem => currentItem + 10,
      list: [],
    }
  ).list
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
  function collectItemSequencesByConditions(collector, item, idx, arr) {
    const { getPredecessor, getSuccessor, list } = collector;
    if (getPredecessor(item) === arr[idx - 1]) {

      // push item into the most recent sequence list.
      list[list.length - 1].push(item);

    } else if (getSuccessor(item) === arr[idx + 1]) {

      // create a new sequence list with its 1st item.
      list.push([ item ]);
    }
    return collector;
  }
</script>