FP方式的数组嵌套映射

时间:2017-10-10 11:32:40

标签: javascript arrays functional-programming ramda.js

给出以下数组:

const array1 = ["a1", "b1", "c1", "d1"],
      array2 = ["a2", "b2"],
      array3 = ["a3", "b3", "c3"]

是否有任何 ramda 函数来简化我可以提供一个或多个数组的以下场景?

const nestedMap = map => {
    const result = []

    for(let item1 of array1) 
        for(let item2 of array2)
            for(let item3 of array3)
                    result.push(map(item1, item2, item3))
    return result
}

整个功能如下:

// Sample usage
nestedMap((item1, item2, item3) => `${item1} ${item2} ${item3}`, array1, array2, array3)

我希望避免重新发明轮子。

注意:Vanilla javascript或任何其他库都可以接受。我最初谈到 ramda ,因为它有很多功能,也许我错过了哪些可以帮助解决这个问题

4 个答案:

答案 0 :(得分:5)

您可以在此处使用数组的应用实例来简化R.lift您的函数:

const array1 = ["a1", "b1", "c1", "d1"],
      array2 = ["a2", "b2"],
      array3 = ["a3", "b3", "c3"]

const nestedMap = R.lift((item1, item2, item3) => `${item1} ${item2} ${item3}`)

console.log(nestedMap(array1, array2, array3))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

答案 1 :(得分:3)

Ramda有一个xprod函数,它提供了两个列表的交叉积。将它扩展到多个列表是相对简单的:

const xproduct = R.reduce(R.pipe(R.xprod, R.map(R.unnest)), [[]])

然后我们可以使用它来相对容易地创建嵌套的map函数:

&#13;
&#13;
const array1 = ["a1", "b1", "c1", "d1"],
      array2 = ["a2", "b2"],
      array3 = ["a3", "b3", "c3"]

const xproduct = R.reduce(R.pipe(R.xprod, R.map(R.unnest)), [[]])
const nestedMap = (fn, ...arrs) => R.map(R.apply(fn), xproduct(arrs))

console.log(nestedMap((a, b, c) => `${a}-${b}-${c}`, array1, array2, array3))
//==> ["a1-a2-a3", "a1-a2-b3", ...]
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
&#13;
&#13;
&#13;

答案 2 :(得分:2)

您可以使用两步法,

  1. 构建所有产品
  2. 映射功能。
  3. &#13;
    &#13;
    const
        nestedMap = (fn, ...array) => array
            .reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []))
            .map(a => fn(...a)),
        array1 = ["a1", "b1", "c1", "d1"],
        array2 = ["a2", "b2"],
        array3 = ["a3", "b3", "c3"],
        result = nestedMap((item1, item2, item3) => `${item1} ${item2} ${item3}`, array1, array2, array3)
    
    console.log(result);
    &#13;
    .as-console-wrapper { max-height: 100% !important; top: 0; }
    &#13;
    &#13;
    &#13;

答案 3 :(得分:2)

如何运作

这个答案通过向.ap的原型 - 直接实现.chain(和Array)作为练习,让您深入了解@ ScottChristopher的回复是如何工作的! 不要惊慌失措,原型狂热分子......

这里的想法是在代码片段中演示目标行为/输出,该代码片段一次显示所有移动部件。只需要约8行代码即可理解,恐吓因素非常低;与在Rambda源中挖掘(实际上相当不错)相比,

我最近分享了another answer使用分隔延续做了类似的事情 - 如果这个答案对你很有意思,我想你也会喜欢那个读^ ^ ^

// Array Applicative  
Array.prototype.ap = function (...args)
  {
    const loop = (acc, [x,...xs]) =>
      x === undefined
        ? [ this [0] (...acc) ]
        : x.chain (a =>
            loop (acc.concat ([a]), xs))
    return loop ([], args)
  }
 
// Array Monad
Array.prototype.chain = function chain (f)
  {
    return this.reduce ((acc, x) =>
      acc.concat (f (x)), [])
  }

const array1 = ['a1', 'b1', 'c1', 'd1']
const array2 = ['a2', 'b2']
const array3 = ['a3', 'b3', 'c3']

console.log ([ (x,y,z) => [x,y,z] ] .ap (array1, array2, array3))
// [ [ 'a1', 'a2', 'a3' ],
//   [ 'a1', 'a2', 'b3' ],
//   [ 'a1', 'a2', 'c3' ],
//   [ 'a1', 'b2', 'a3' ],
//   [ 'a1', 'b2', 'b3' ],
//   [ 'a1', 'b2', 'c3' ],
//   [ 'b1', 'a2', 'a3' ],
//   [ 'b1', 'a2', 'b3' ],
//   [ 'b1', 'a2', 'c3' ],
//   [ 'b1', 'b2', 'a3' ],
//   [ 'b1', 'b2', 'b3' ],
//   [ 'b1', 'b2', 'c3' ],
//   [ 'c1', 'a2', 'a3' ],
//   [ 'c1', 'a2', 'b3' ],
//   [ 'c1', 'a2', 'c3' ],
//   [ 'c1', 'b2', 'a3' ],
//   [ 'c1', 'b2', 'b3' ],
//   [ 'c1', 'b2', 'c3' ],
//   [ 'd1', 'a2', 'a3' ],
//   [ 'd1', 'a2', 'b3' ],
//   [ 'd1', 'a2', 'c3' ],
//   [ 'd1', 'b2', 'a3' ],
//   [ 'd1', 'b2', 'b3' ],
//   [ 'd1', 'b2', 'c3' ] ]