创建一个映射回调,按数组和缩放每个数组值

Create a map callback to scale each array value by array sum

本文关键字:数组 缩放 回调 一个 映射 创建      更新时间:2023-09-26

我的目标是写一个回调函数…

function proportionize(val,ind,arr){
   ...
}

这样当提供给Array.prototype时。映射时,返回数组中的每个值各自占数组总数的比例。

例如,

let myArray = [1,2,3] // Sums to 6
let proportions = myArray.map( proportionalize ) //  [1/6, 2/6, 3/6]

我希望这是…

  • 自包含 -我不想提前计算总和
  • 有效率 -我不想在每次迭代时重新计算总和
  • 可与其他fn的互换-参见下面的编辑

这个问题很简单没有这些条件。即使有了这3个标准,我仍然觉得有一些简单/明显的方法可以做到这一点(也许是尝试在每次迭代中共同的闭包中"保存"总和),但出于某种原因,我还是一片空白。什么好主意吗?

测试工作回答…

let arr = [1,2,3],
    callbacks = [Math.sqrt, Math.round, Math.sin, **proportionalize**, etc...];
for(cb in callbacks){
  console.log(arr.map(callbacks[cb]))
}

编辑

还有,作为参考,有一个非回答"工作",但我会考虑一个非回答,因为它需要传递一个参数给一个函数,然后生成回调。我希望对map的调用与对任何其他函数的调用相匹配。例如…

// The "working" non-answer
function proportionalize(arrIn){
  let total = arrIn.reduce( (a,b)=>a+b);
  return (val,ind,arr)=>{ return val/total };
}
myArray.map(proportionalize(myArray)) // works but I'd have to call differently than...
myArray.map(Math.sqrt)

这个答案是有效的,但是有一些我不喜欢的地方…

方法:

  • 只计算第一次迭代的总数
  • 将总数"保存"到数组,arr.total(每次迭代都可用)
  • 在地图迭代中使用arr.total
  • 在返回数组之前删除最后一次迭代的total属性

代码:

function proportionalize(val, ind, arr){
  if(ind===0){
    arr.total = arr.reduce( (a,b)=>a+b);
  }
  let proportion = val/arr.total;
  if(ind===(arr.length-1)){
    delete arr.total;
  }
  return proportion;
}
[1,2,3].map(proportionalize) // [ 0.16666666666666666, 0.3333333333333333, 0.5 ]

我不喜欢的:

我正在修改数组,添加和删除"total"属性。一般来说,我不想假设属性没有被用于其他用途。该属性名称可以更改为随机的字母字符串或其他东西,但这仍然感觉有点粗糙。

请记住,[1,2,3].map(proportionalize)proportionalize应用于数组的每个元素。这不是对数组本身的操作,而是对数组元素的操作。

虽然这不是一个"map回调",你现有的解决方案可以包装如下:

Array.prototype.proportionalize = function() {
  let sum = this.reduce((a,b) => a+b);
  return this.map(x => x/sum);
}
例如:

> [1,2,3].proportionalize()
[ 0.16666666666666666, 0.3333333333333333, 0.5 ]

这个问题可以更一般地问…

当我用给定的回调函数调用map时,我如何在每个回调函数的实例/调用之间创建/利用共享状态?一旦这个问题得到了答案,你就可以使用短路计算之类的方法来防止不必要的重复计算。

我想到Array.prototype.map已经通过可选的thisArg提供了此功能。如果this还没有被使用,提供一个空对象{},允许回调调用修改/共享相同的this,而不修改在map调用生命周期之外持续存在的状态。

从这个空状态开始,让我们只计算未计算过的总数,并且每次迭代都可以访问总数。

proportionalize = function(val, ind, arr){
  this.total = this.total || arr.reduce((x,y)=>x+y);
  return val/this.total;
}
let arr = [1,2,3],
    callbacks = [Math.round, proportionalize];
for(cb in callbacks){
  console.log(arr.map(callbacks[cb],{})) 
// => [1,2,3], [ 0.16666666666666666, 0.3333333333333333, 0.5 ]
}
arr = [1,2,7];
for(cb in callbacks){
  console.log(arr.map(callbacks[cb],{}))
// => [1,2,3], [ 0.1, 0.2, 0.7 ]
}