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

Reducing a 'raw' javascript array into a more compact array (of objects)

本文关键字:数组 对象 转换 原始 javascript 减少      更新时间:2023-09-26

在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 },
//   ...
// ]

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

我正在寻找一个非常像reduce的解决方案,除了能够减少到一个以上的值。

高性能的好处是,但最终解决方案应该可读、可维护和可用。

您仍然可以使用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个参数的函数。

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

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的行为,即它修改数组以删除指定的元素,并返回已删除元素的数组。

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

Array.prototype.reduceMulti = function(n, reducer) {
    return partition(this, n).map(reducer);
}
var output = input.reduceMulti(3, formatData);

将问题分解为几个步骤:对平面数组进行分组,使用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匹配可以改进优化——我告诉大家)。

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