【问题标题】:How to split an array into a number of arrays based on increasing value by a given number?如何根据给定数字的增加值将数组拆分为多个数组?
【发布时间】:2021-10-08 03:39:53
【问题描述】:

我写了以下函数:

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 的数字序列,那么这将返回一个包含这些值的新数组。 例如:[1, 2, 3, 6, 10] 将返回 [1, 2, 3]

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

【问题讨论】:

    标签: javascript arrays algorithm reduce


    【解决方案1】:

    您可以让累加器成为部分结果数组和前一个值的组合(以便于访问它)。为了保持代码简单,您可以将每个当前值添加到最后一个子数组中,或者作为它自己的子数组。 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);

    【讨论】:

      【解决方案2】:

      一种简单的方法,基于两个条件,其优先级不能更改/交换,实际上也可以读取它的作用......

      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的Q

      我设置了两对条件,一对为- 1+ 1,第二对分别为- 10+ 10。 hits 数组是[22, 31, 32, 33, 42, 52]。我把你的console.logs 变成了const variable = hits.reduce... 等等。然后我返回了两个变量。结果是[31, 32, 33][42, 52]。第二个的预期结果当然是[22, 33, 42, 52]

      首先,OP 很可能是指[22, 32, 42, 52]

      其次...

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

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

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

      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>

      【讨论】:

      • 我在我的程序中采用了您的解决方案。它就像一个魅力,谢谢。对我来说不太可读的一件事是解构的使用。我是一个初学者,我从未见过有人以对象的形式在 reduce 中使用累加器。您能否建议我阅读以更清楚地理解这一点?
      • @esiale ... 1/2 ... 我假设,您指的是通用的第二种方法。只有在有实际用例时才使用它。否则,坚持第一个。至于累加器和解构......好吧,累加器/收集器可以是任何想要的东西,对于数值求和,它将是一个有限数值,对于连接字符串,它将是一个字符串值,同样对于目标格式数组和对象。当然,没有规则不允许将累加器/收集器对象用作config 之类的对象,用于更通用、可定制的用例。
      • @esiale ... 2/2 ... 只需要编写足够可读的代码并对配置进行注释,以明确哪些是可定制部分以及哪些属性/键config/collector/accumulator 是实际结果。
      • 你是雏菊。有趣的是,我在您的第二种方法中遇到了一些问题。 (遗憾的是,我无法在此处粘贴代码)我将整个方法包装到一个函数中,该函数采用单个命中参数。我设置了两对条件,一个为 item - 1,item + 2,第二对分别为 - 10,+ 10。命中数组为[22, 31, 32, 33, 42, 52]。我把你的 console.logs 变成了 const variable = hits.reduce... 等等。然后我返回了两个变量。结果是 [31, 32, 33] 和 [42, 52]。第二个的预期结果当然是 [22, 33, 42, 52]。发生了一些奇怪的事情
      • 我无法编辑,这是一个错字,我的意思是 - 1, + 1
      猜你喜欢
      • 1970-01-01
      • 2021-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-21
      • 2013-12-04
      • 2011-06-15
      • 1970-01-01
      相关资源
      最近更新 更多