按值对相邻数组项进行分组的最佳方法

时间:2014-10-31 13:20:08

标签: javascript arrays functional-programming

假设我们有一组值:

[5, 5, 3, 5, 3, 3]

按价值和邻接对它们进行分组的最佳方法是什么。结果应如下:

[ [5,5], [3], [5], [3,3] ]

当然,我可以遍历源数组并查找下一个/上一个项目,如果它们相同,则将它们推送到一个临时数组,然后将其推送到生成的数组。

但我喜欢以功能方式编写代码。那么也许有更好的方法呢?

4 个答案:

答案 0 :(得分:5)

您可以使用Array.prototype.reduce方法:



var result = [5, 5, 3, 5, 3, 3].reduce(function(prev, curr) {
    if (prev.length && curr === prev[prev.length - 1][0]) {
        prev[prev.length - 1].push(curr);
    }
    else {
        prev.push([curr]);
    }
    return prev;
}, []);

alert( JSON.stringify(result) );




答案 1 :(得分:1)

如果你在谈论性能,n - 复杂性[O(n)]就是你得到的(你正在讨论的一次迭代)。

如果您正在谈论内存使用优化,可能您需要推送/弹出对象(而不是复制它们)。或者尝试在首次创建时将输入数组组织为数组数组(例如[[5],[5],[3],[5],[3],[3]])并且只对此工作数组,将其塑造成你想要的最终形式。

如果您想在实现中更加花哨,可以尝试实现递归函数来处理它(但作为一般经验法则,在两个提供相同性能的实现之间,那个更容易阅读的是你应该瞄准的那个。)

答案 2 :(得分:1)

我也需要相同的东西,但是需要根据每个值的功能(而不是值本身)进行分组。例如,

[1, 3, 5, 2, 4, 6, 7, 8, 9, 10, 42]

成为

[[1, 3, 5], [2, 4, 6], [7], [8], [9], [10, 42]]

具有分组功能

function (a) { return a % 2; }

一种方法是扩展@dfsq的方法,但使用按字典顺序定义的fn_prev以避免重新计算先前的fn(val)

var fn_prev = null;
myArray.reduce((acc, val) => {
  let fn_curr = fn(val);
  if (!acc.length || fn_curr !== fn_prev) {
    acc.push([val]);
  } else {
    acc[acc.length - 1].push(val);
  }
  fn_prev = fn_curr;
  return acc;
}, []);

我觉得避免.reduce更为简单:

let fn_prev = null;
let acc = [];
for (let val of myArray) {
  let fn_curr = fn(val);
  if (!acc.length || fn_curr !== fn_prev) {
    acc.push([val]);
  } else {
    acc[acc.length - 1].push(val);
  }
  fn_prev = fn_curr;
}
console.log(acc);

可以像这样将其添加到Array原型中:

Object.defineProperty(Array.prototype, 'groupByAdjacent', {
  value: function(fn) {
    let fn_prev = null;
    let acc = [];
    for (let val of [].concat(this)) {
      let fn_curr = fn(val);
      if (!acc.length || fn_curr !== fn_prev) {
        acc.push([val]);
      } else {
        acc[acc.length - 1].push(val);
      }
      fn_prev = fn_curr;
    }
    return acc;
  }
});

并以此方式使用:

let x = [1, 3, 5, 2, 4, 6, 7, 8, 9, 10, 42];
let grouped_x = x.groupByAdjacent(a => a % 2);
console.log(grouped_x);

答案 3 :(得分:1)

对于罕见的Array.prototype.reduceRight-

这是一个很好的用例

const data =
  [ 5, 5, 3, 5, 3, 3 ]
  
const groupAdjacent = (a = []) =>
  a.reduceRight
    ( ([ group = [], ...result ], v) =>
        group.length
          ? group[0] === v
              ? [ [ v, ...group ], ...result ] // v matches group
              : [ [ v ], group, ...result ]    // v does not match group
          : [ [ v ], ...result ]               // no group to compare
    , [[]]
    )
        
console.log(groupAdjacent(data))
// [ [ 5, 5 ], [ 3 ], [ 5 ], [ 3, 3 ] ]

console.log(groupAdjacent([]))
// [ [] ]

可以使用延续传递样式来描述相同的过程-

const data =
  [ 5, 5, 3, 5, 3, 3 ]
  
const identity = x =>
  x
  
const None =
  Symbol ()
  
const groupAdjacent = ([ v = None, ...more ], k = identity) =>
  v === None
    ? k ([[]])
    : groupAdjacent (more, ([ group, ...result ]) =>
        group.length
          ? group[0] === v
              ? k ([ [ v, ...group ], ...result ]) // v matches group
              : k ([ [ v ], group, ...result ])    // v does not match group
          : k ([ [ v ], ...result ])               // no group to compare
      )
        
console.log(groupAdjacent(data))
// [ [ 5, 5 ], [ 3 ], [ 5 ], [ 3, 3 ] ]

console.log(groupAdjacent([]))
// [ [] ]