Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested map of arrays the FP way

Given the following arrays:

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

Is there any ramda function to simplify the following scenario on which I could give one or more arrays?

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
}

Whole function would look as follows:

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

I'm looking to avoid reinventing the wheel.

Note: Vanilla javascript or any other library can be acceptable. I initially talked about ramda as it has a lot of functions and maybe I've missed which could assist on solving this problem

like image 965
Matías Fidemraizer Avatar asked Oct 10 '17 11:10

Matías Fidemraizer


3 Answers

You can use the applicative instance for arrays here to simply R.lift your function:

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>
like image 200
Scott Christopher Avatar answered Nov 19 '22 06:11

Scott Christopher


Ramda has a xprod function that gives the cross-product of two lists. It's relatively straightforward to extend it to multiple lists like this:

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

Then we can use this to create the nested map function relatively easily:

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", ...]
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
like image 3
Scott Sauyet Avatar answered Nov 19 '22 06:11

Scott Sauyet


You could use a two step approach,

  1. build all products
  2. map the function.

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);
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 2
Nina Scholz Avatar answered Nov 19 '22 06:11

Nina Scholz