Many Maps
I briefly covered a mapping function in "The Functional Shape of Loops" which can be used for one-to-one mapping, meaning that one item in the input collection produces one value in the output collection:
[1, 2, 3] |> map((n) => n * 2); // [2, 4, 6]
Sometimes it's useful to have a one-to-many mapping, where one input produces multiple output values.
With the classic map function the output becomes a collection of collections:
[1, 2, 3] |> map((n) => [n, n * 2]); // [[1, 2], [2, 4], [3, 6]]
Sometimes lists of lists are useful, but quite often it's more helpful to have a flattened collection of nodes.
This can be handled with a simple reducer function:
[1, 2, 3]
|> map((n) => [n, n * 2])
|> reduce((acc, x) => acc.concat(x));
// [1, 2, 2, 4, 3, 6]
However, given how common one-to-many mappings are, it would be useful to be able to express this transformation in a single function.
The original map function has an implementation of:
const map = (fn) =>
function* (collection) {
let i = 0;
for (const value of collection) {
yield fn(value, i++);
}
};
where yield returns a single value. If we instead use yield *, each collection returned by fn will automatically be concatenated into a single output collection:
const mapMany = (fn) =>
function* (collection) {
let i = 0;
for (const value of collection) {
yield* fn(value, i++);
}
};
Which allows our implementation of the previous example to turn into:
[1, 2, 3] |> mapMany((n) => [n, n * 2]); // [1, 2, 2, 4, 3, 6]
