如何使用纯函数式编程对数组元素进行分组?

时间:2016-09-21 11:10:50

标签: arrays functional-programming

假设我有一个包含多个元素的数组arr。我想创建一个由arr(数组数组)组成的数组的数组,其中每个组由匹配某个条件fn的下一个元素和顺序中的每个下一个元素组成,直到下一个匹配。

所以我想通过匹配元素开始分组数组。 我做了以下代码来做到这一点,但这对我来说太迫切了:

var sliceGroupingBy = (arr, fn) => {
  var newArray = [];
  arr.forEach(el => {
    if (fn(el)) {
      newArray.push([el]);
    } else {
      newArray.slice(-1)[0].push(el);
    }
  });
  return newArray;
};

示例:

var in = [1, 2, 4, 6, 3, 4, 6, 5, 7, 8, 8, 1];
var out = sortaGroupBy(in, x => x % 2 === 1);
// [ [ 1, 2, 4, 6 ], [ 3, 4, 6 ], [ 5 ], [ 7, 8, 8 ], [ 1 ] ]

我想知道更有效的方法是什么,只使用常规功能操作(没有if或aux。数组)。

2 个答案:

答案 0 :(得分:2)

您可以使用reduce而不是某些命令性循环来使其更具功能性:

function sliceGroupingBy(arr, fn) {
  return arr.reduce((newArray, el) => {
    if (fn(el))
      newArray.push([el]);
    else
      newArray[newArray.length-1].push(el);
    return newArray;
  }, []);
}

如果您还想避免使用push,无论出于何种原因,您都可以concat

const sliceGroupingBy = (arr, fn) =>
  arr.reduce((newArray, el) =>
    fn(el)
      ? newArray.concat([[el]]);
      : newArray.slice(0, -1).concat(newArray.slice(-1).map(lastArray =>
          lastArray.concat([el])
        )
  , []);

(“改变”数组的最后一个元素可以通过多种方式完成)

答案 1 :(得分:0)

总体效率低下但是:

let predicate = x => x % 2 === 1;
let partition = (pred, arr) => {
  let res1 = arr.filter(pred);
  let res2 = arr.filter(x => !pred(x));
  return [res1, res2];
};

partition(predicate, [1,2,3,...]);

我永远不会在JavaScript中这样做(从代码中假设有问题的语言)。请参阅我对您问题的评论。

修改

根据我对你正在做的事情的错误评论,

let xs = array.reduce((acc, x, i, arr) => {
  let lastIndex = acc.reduce((y, z) => { y + z.length }, 0);
  return predicate(x) || i === arr.length - 1 ?
    acc.concat([arr.slice(lastIndex, i)]) :
    acc;
}, []);

不使用语句,仅使用表达式,不使用变异,仅使用复制。如果不使用immutable.js或mori,我仍然不会这样做。它相当昂贵。

编辑2

基于Bergi评论的最后一种方法的更高效版本:

let {result} = array.reduce((obj, x, i, arr) => {
  let {lastIndex, result} = obj;
  let passes = predicate(x) || i === arr.length - 1;
  return {
    lastIndex: passes ? i : lastIndex,
    result: passes ? result.concat([arr.slice(lastIndex, i)]) : result
  };
}, { lastIndex: 0, result: [] });