使用函数式编程平均2D数组中的列

时间:2018-03-02 04:24:51

标签: javascript arrays node.js loops functional-programming

作为函数式编程的练习,我决定通过我的一个项目并用Array.prototype高阶函数替换包含for循环的函数例如mapreduce

我的项目中的一个函数在二维数组中平均列。它需要一个参数samples,它是一个大小为[n][LOOKBACK]的二维数组:

[
    [0.6,  4.0, -0.5],
    [1.0, -0.5, -0.8],
    ...
]



const LOOKBACK = 3

function averageChange(samples) {
  let result = []
  let count = 0,
    i, j

  for (i = 0; i < LOOKBACK; i++) {

    let accumulator = 0

    for (j = 0; j < samples.length; j++) {
      accumulator += samples[j][i]
    }

    result.push(accumulator / samples.length)
  }

  return result
}

console.log(
  averageChange([
    [0.6, 4.0, -0.5],
    [1.0, -0.5, -0.8]
  ])
)
&#13;
&#13;
&#13;

输出应该是一个大小为LOOKBACK的数组,其元素是每列的平均值:

[0.8, 1.75, -0.65]

我花了一些时间试图找到解决方案,但我似乎无法想出一个解决方案。

这是否可以使用Javascript内置的数组函数?

*更新

来自Kirill的优雅解决方案。如果其他人有一个很好的解决方案,我很乐意看到更多。

3 个答案:

答案 0 :(得分:2)

使用reduceforEach函数尝试此示例:

&#13;
&#13;
let a = [
    [0.6,  4.0, -0.5],
    [3.0, -0.5, -0.1],
    [1.0, -0.2, -0.8],
    [7.0, -0.5, -0.8]
];

let b = a.reduce((acc, cur) => {
    cur.forEach((e, i) => acc[i] = acc[i] ? acc[i] + e : e);
    return acc;
}, []).map(e => e / a.length);

console.log(b);
&#13;
&#13;
&#13;

这是使用矩阵转置的更狡猾的方法:

&#13;
&#13;
let a = [
    [0.6,  4.0, -0.5],
    [3.0, -0.5, -0.1],
    [1.0, -0.2, -0.8],
    [7.0, -0.5, -0.8]
];

let b = a[0].map((col, i) => a.map(row => row[i]).reduce((acc, c) => acc + c, 0) / a.length);

console.log(b);
&#13;
&#13;
&#13;

答案 1 :(得分:2)

介绍..

功能编程不只是编写单行并使用高阶函数,例如Arary#mapArray#reduce和{{3} }。顺便说一句,Array#filter不起作用,因为它不是 Array#forEach ..

高阶函数外,您还可以使用pure functioncurryingfunction composition

算法

我们要做的是:

  1. 重新排列矩阵
  2. 计算矩阵内每个数组的平均值
  3. 这可以在JavaScript中看起来像:

    const averageChange = pipe(
        rearrange ([]),
        map (average) 
    )
    

    pipe是将组合多个函数组合成一个巨大函数的函数。 averageChange现在接受一个参数,这将通过管道。

    重新整理

    const rearrange = yss => xss => 
        xss[0].length === 0
            ? yss
            : rearrange
                (concat (yss) ([ map ( getIndex (0) ) ( xss ) ]))
                (map ( slice (1, xss[0].length) ) ( xss ))
    

    这看起来很神秘。由于咖喱和功能组成,我们可以重写它:

    const rearrange = yss => xss => 
        matrixLength (xss) === 0
            ? yss
            : rearrange
                (concat (yss) ([ firstIndeces ( xss ) ]))
                (excludeFirstIndeces ( xss ))
    

    rearrange是一个递归函数,用于转换矩阵

    [
        [0.6,  4.0, -0.5],
        [3.0, -0.5, -0.1],
        [1.0, -0.2, -0.8],
        [7.0, -0.5, -0.8]
    ]
    

    [ 
        [ -0.5, -0.1, -0.8, -0.8 ],
        [  4  , -0.5, -0.2, -0.5 ],
        [  0.6,  3  ,  1  ,  7   ] 
    ]
    

    工作代码示例

    我编写了比其他解决方案更多的代码,但是我将逻辑划分为我自己的函数,这意味着我们现在可以将average这样的函数用于代码的其他部分。另外,我已经为Array#map等编写了curryied版本来编写它们。如果你使用一个库,这将是多余的..

    &#13;
    &#13;
    // helper functions
    const pipe = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)))
    
    const getIndex = i => xs => 
        xs[i]
    
    const map = f => xs =>
        xs.map(f)
    
    const reduce = f => seel => xs =>
        xs.reduce(f)
    
    const concat = ys => xs =>
        xs.concat(ys)
    
    const slice = (start, end) => xs =>
        xs.slice(start, end)
    
    const average = xs =>
        reduce ((sum, x) => sum + x) (0) (xs) / xs.length
        
    const length = xs =>
      xs.length
        
    const matrixLength = pipe(
      getIndex(0),
      length
    )
    
    const firstIndex = getIndex (0)
    
    const firstIndeces = map ( firstIndex )
    
    const excludeFirstIndex = xss => slice (1, matrixLength (xss)) (xss)
    
    const excludeFirstIndeces = map ( excludeFirstIndex )
      
       
    // business logic 
    const rearrange = yss => xss => 
        matrixLength (xss) === 0
            ? yss
            : rearrange
                (concat (yss) ([ firstIndeces ( xss ) ]))
                (excludeFirstIndeces ( xss ))
    
    const averageChange = pipe (
        rearrange ([]),
        map(average) 
    )
    
    const values = [
        [0.6,  4.0, -0.5],
        [3.0, -0.5, -0.1],
        [1.0, -0.2, -0.8],
        [7.0, -0.5, -0.8]
    ]
    
    console.log( averageChange (values) )
    &#13;
    &#13;
    &#13;

答案 2 :(得分:0)

var a=[];var b=[];
var i = 0; j = 0;

cubes.forEach(function each(item,length) {
  if (Array.isArray(item)) {
    item.forEach(each);
    j++;
    i = 0;
  } else {
    if(a[i]===undefined){
        a[i]=0;b[i]=0}
    a[i]=(a[i]*b[i]+item)/(b[i]+1);
    b[i]=b[i]+1;
    i++;
  }
});
console.log(a);

这甚至可以用于不同的内部数组大小。这样的东西[[1],[2,3,4]]

cubes=[[1],[1,2,3]]
var a=[];var b=[];
var i = 0; j = 0;

cubes.forEach(function each(item,length) {
  if (Array.isArray(item)) {
    item.forEach(each);
    j++;
    i = 0;
  } else {
    if(a[i]===undefined){
        a[i]=0;b[i]=0}
    a[i]=(a[i]*b[i]+item)/(b[i]+1);
    b[i]=b[i]+1;
    i++;
  }
});
console.log(a);