2014-10-07 30 views
2

在javascript中,我正在寻找一种处理数组的方法,使集合中的短重复序列(元组)减少到更紧凑集合。我想将一个长度为3n的数组转换成长度为的集合,其中将“原始”javascript数组缩减为一个更紧凑的(对象)数组

下面是一个假想的例子:

// 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的解决方案,除了能够降低多个值。

高性能奖励点,但最终解决方案应该是可读的,可维护的和可用的。

+0

做你打算做'输入'变量列表的清单还是现有的分配权? – Hrishi 2014-10-07 04:27:43

+0

@Hrishi'input'数组只是一个简单的一维数组,格式化为使三个组更清晰。 (它是正确的。) – aaaidan 2014-10-07 04:36:30

+0

你想要更多...本地数组函数或速度?一个'for'循环通常要快得多。 – 2014-10-07 04:56:49

回答

3

你仍然可以使用reduce这也需要一个索引和数组作为参数:如果你给n3一个值,那么你必须传递预计3个参数的函数

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]] 

+0

不错!很高兴看到用于解决这个问题的本地reduce函数。很遗憾,你不能跳过“回报”电话。 – aaaidan 2014-10-07 04:40:27

+0

如果我给这个数组的元素数量不是'n'的确切倍数的数组,那么它会在最后一个分区结束时返回undefined。它也不适用于其他分区大小。这可以通过改变'formatData'来简单地返回'arguments'来解决。从设计的角度来看,我也不相信将分区的概念与以某种方式将每个分区中的元素转换(通过'formatData')的概念相结合是理想的。拥有一个独立的分区器似乎会更好,然后让用户在分区上做任何他想做的事情。 – 2014-10-08 03:18:33

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); 

额外的正式参数看起来怪异,但他们做的函数运行速度比他们其他明智的将更快(我只是发现了元数匹配改善优化 - 我告诉大家)。

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

+0

最初的阵列是平坦的。 – 2014-10-07 06:07:20

+1

感谢您的答案,但计算最小值,最大值,平均值,不是我想要实现的。再次阅读这个问题,让我知道你怎么想我可以更清楚。 – aaaidan 2014-10-07 06:08:47

+0

@aaaidan:我编辑过......这个逻辑是可以热插拔的,只需将一个不同的函数传递给map()而不是report();如果你使用可互换的通用零件,你不需要预先构建任何东西... – dandavis 2014-10-07 06:17:16

2

我就开始用一个简单的小程序来划分的阵列分成大小相同的部分:

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

还有一点SUM函数:

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}; 
    }; 
}) 

如果您希望使用非rec ursive分段:

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

这需要的Array#splice行为的优点,其修饰的数组中删除指定的元素,并返回所移除元件的阵列。

如果你真的想实现正是你提出的语法,那么你就需要把reduceMulti该数组的原型,而不是我,建议:

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

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

哦哇,递归分区函数!既然在我知道的任何浏览器中没有尾递归,那么对于大数据集你会得到一个...... _stack溢出吗? – aaaidan 2014-10-07 23:26:32

+0

@aaaidan不仅递归和容易发生堆栈溢出,而且破坏性 - 你的数组奇迹般变为空! – 2014-10-08 02:07:14

+0

哇,好卖的老兄! – aaaidan 2014-10-08 02:54:34

相关问题