在这个问题中,我遇到了以下简化问题:
我们从一个带有value属性的Objects数组开始.我们想要为每个值计算它的总和的百分比,并将其作为属性添加到结构中.要做到这一点,我们需要知道值的总和,但这个总和不是事先计算的.
//Original data structure [ { "value" : 123456 }, { "value" : 12146 } ] //Becomes [ { "value" : 123456, "perc" : 0.9104 }, { "value" : 12146 , "perc" : 0.0896 } ]
一个简单且可能最具可读性的解决方案是两次遍历数据结构.首先我们计算总和,然后计算百分比并将其添加到数据结构中.
var i; var sum = 0; for( i = 0; i < data.length; i++ ) { sum += data[i].value; } for( i = 0; i < data.length; i++ ) { data[i].perc = data[i].value / sum; }
我们是否可以只通过数据结构一次,并以某种方式告诉只有在知道整个总和后才能评估百分比表达式?
我主要对解决纯javascript问题的答案感兴趣.那就是:没有任何库.
具有自修改代码的解决方案.
它将f
计算功能移动到迭代结束,然后通过链接函数分配单个项目的百分比.
var data = [
{ "value": 123456 },
{ "value": 12146 },
];
data.reduceRight(function (f, a, i) { // reduceRight, i=0 is at the end of reduce required
var t = f; // temporary save previous value or function
f = function (s) { // get a new function with sum as parameter
a.perc = a.value / s; // do the needed calc with sum at the end of reduce
t && t(s); // test & call the old func with sum as parameter
};
f.s = (t.s || 0) + a.value; // add value to sum and save sum in a property of f
i || f(f.s); // at the last iteration call f with sum as parameter
return f; // return the function
}, 0); // start w/ a value w/ a prop (undef/null don't work)
document.write('' + JSON.stringify(data, 0, 4) + '
');
此解决方案使用单个循环来计算总和,并perc
使用以下内容在每个元素上放置计算属性getter
:
function add_percentage(arr) { var sum = 0; arr.forEach(e => { sum += e.value; Object.defineProperty(e, "perc", { get: function() { return this.value / sum; } }); }); }
一个直截了当的推迟就是
function add_percentage(arr) { var sum = 0; arr.forEach(e => { sum += e.value; setTimeout(() => e.perc = e.value / sum); }); }
但是,这样做到底有什么意义呢?
例如,用一个较少的循环来实现这一点的方法是写出由所有可能的项组成的整个sum语句
var sum = (data[0] ? data[0].value : 0) + (data[1] ? data[1].value : 0) + (data[2] ? data[2].value : 0) + ... (data[50] ? data[50].value : 0); for( i = 0; i < data.length; i++ ) { data[i].perc = data[i].value / sum; }
并非这实际上是一个真正的解决方案
您可以使用Array的reduce函数,但它仍然是后台循环,并且每个数组元素都有一个函数调用:
var sum = data.reduce(function(output,item){ return output+item.value; },0); for( i = 0; i < data.length; i++ ) { data[i].perc = data[i].value / sum; }
你可以使用ES6 Promise,但是你还在添加一堆函数调用
var data = [ { "value" : 123456 }, { "value" : 12146 } ] var sum = 0; var rej = null; var res = null; var def = new Promise(function(resolve,reject){ rej = reject; res = resolve; }); function perc(total){ this.perc = this.value/total; } for( i = 0; i < data.length; i++ ) { def.then(perc.bind(data[i])); sum+=data[i].value; } res(sum);
Perf测试
加成声明
10,834,196
±0.44%
最快减少
3,552,539
±1.95%减少
67%承诺
26,325
±8.14
%慢100%对于循环
9,640,800
±0.45
%慢11%