减少原始'将javascript数组转换为更紧凑的数组(对象)

时间:2014-10-07 04:12:25

标签: javascript arrays functional-programming

在javascript中,我正在寻找一种处理数组的整洁方式,以便集合中的短,重复序列(元组)变为更紧凑的集合。我想将长度 3n 的数组转换为长度 n 的集合。

这是一个想象中的例子:

// A flat (1-dimensional) array of numbers for input
var input = [ 
  1, 11, 5,
  2, 12, 10,
  3, 13, 6,
  4, 14, 11,   
  ... ];

// A custom "lambda". This is my main intent, the rest is just plumbing.
var formatData = function(a,b,c) {
  return { foo: a, bar: b, woot: c };
};

var output = input.reduceMulti(3, formatData); // ficticious 'reduceMulti'!

// output: 
// [
//   { foo: 1, bar: 11, woot: 5  },
//   { foo: 2, bar: 12, woot: 10 },
//   { foo: 3, bar: 13, woot: 6  },
//   { foo: 4, bar: 14, woot: 11 },
//   ...
// ]

或者,output中的那些对象很容易成为字符串或数组,如果formatData返回不同的内容。

我正在寻找一种类似reduce的解决方案,除了能够减少多个值。

获得高效的奖励积分,但最终解决方案应该是可读,可维护和可用的。

3 个答案:

答案 0 :(得分:3)

您仍然可以使用reduce,它还将索引和数组作为参数:

var formatData = function(x, y, z) {
  return [x, y, z]
}

var reduceMulti = function(n, f, xs) {
  return xs.reduce(function(acc, x, i, xs) {
    if (i % n === 0) {
      acc.push(f.apply(null, xs.slice(i, i+n)))
    }
    return acc
  },[])
}

reduceMulti(3, formatData, input)
//^ [[1,11,5], [2,12,10], [3,13,6], [4,14,11]]

如果您为n提供3的值,那么您必须传递一个需要3个参数的函数。

答案 1 :(得分:2)

我将从一个简单的小例程开始,将数组划分为相同大小的段:

function partition(arr, n) {
    return arr.length ? [arr.splice(0, n)].concat(partition(arr, n)) : [];
}    

还有一点功能:

function sum(arr) { return arr.reduce(function(s, v) { return s + v; }); }

现在,我们将对原始数组进行分区并将其映射到统计度量对象:

partition(input, 3).map(function(seg) {
    return {
        max:  Math.max.apply(0, seg), 
        min:  Math.min.apply(0, seg), 
        mean: sum(seg)/seg.length};
    };
})

如果您更喜欢非递归分段器:

function partition(arr, n) {
    var result = [];
    while (arr.length) { result.push(arr.splice(0, n)); }
    return result;
}

这利用了Array#splice的行为,它修改了数组以删除指定的元素并返回已删除元素的数组。

如果你真的想要完全实现你提出的语法,那么你需要将reduceMulti放在Array原型上,而不是我建议的那样:

Array.prototype.reduceMulti = function(n, reducer) {
    return partition(this, n).map(reducer);
}

var output = input.reduceMulti(3, formatData);

答案 2 :(得分:1)

将问题分解为多个步骤:对平面数组进行分组,使用map()对每个数组进行转换,然后使用reduce计算总和。

var r= [ 
  1, 11, 5,
  2, 12, 10,
  3, 13, 6,
  4, 14, 11,
 ];

function flatToGrouped(r, width){
  var o=[];
  for(var i=0, mx=r.length; i<mx; i+=width){
   o.push(r.slice(i, i+width) );
  }
  return o.filter(Boolean);
}

function report(a,_,__){
  return { 
    min:  Math.min.apply(0,a), 
    max:  Math.max.apply(0,a), 
    mean: a.reduce( function(a,b,_,__){ return a+b; } ) 
  }
}

flatToGrouped(r, 3).map(report);

额外的形式参数看起来很奇怪,但它们使得函数运行速度比其他方式更快(我发现arity匹配改进了优化 - 我告诉所有人)。

编辑:我没有意识到最初的阵列是扁平的。您仍然可以在项目转换器上使用简单映射,并使用收集器来生成要分析的n级数组。通过将它们分成两部分,你不会被锁定为reduce,而是数组可以做的任何事情(map / reduce / filter / etc)。