Ramda's mapAccum function
February 01, 2022
Ramdajs is JavaScript library for functional programming. Ramda makes it easy to create functional pipelines, where the output of one function is piped into the input of the next. Ramda allows you to write complex logic by combining simple functions. Ramda functions never mutate data so you won’t have to think about shared mutable state and all functions are free from side-effects. They don’t interact with any state outside of the function. This makes the code predictable and easier to understand.
The missing child of map and reduce
JavaScript developers are familiar with the build-in array methods map and reduce and Ramda’s mapAccum function is closely related to them. It is like reduce in that it outputs an accumulator, a value that is updated by each item in the array. and similar to map it outputs all the intermediate values.
Code examples
Ramdas map and reduce functions are not part of the array prototype. Instead they accept the array as the last argument.
const integers = [1, 2, 3, 4];const addOne = value => value + 1;map(addOne, integers) // [1 + 1, 2 + 1, 3 + 1, 4 + 1] => [2, 3, 4, 5]const addToAcc = (value, acc) => value + acc;reduce(addToAcc, 0, integers) // 0 + 1 + 2 + 3 + 4 => 10
Ramdas mapAccum function accepts a function that takes a value and accumulator and outputs a tuple of value and accumulator. mapAccum outputs a tuple of the calculated value and an array of the intermediate values of the accumulator.
const integers = [1, 2, 3, 4];const mapAccumFn = (value, acc) => [value + acc, value + acc];mapAccum(mapAccumFn, 0, integers); // [10, [1, 3, 6, 10]]
You can play around with these functions in the Ramda playground.
A random walk with mapAccum
Here’s a random walk implemented using Ramda’s mapAccum function.
function moveRandomly(point: [number, number]) {const directions = {n: ([x, y]) => [x, y + 1],ne: ([x, y]) => [x + 1, y + 1],e: ([x, y]) => [x + 1, y],se: ([x, y]) => [x + 1, y - 1],s: ([x, y]) => [x, y - 1],sw: ([x, y]) => [x - 1, y - 1],w: ([x, y]) => [x - 1, y],nw: ([x, y]) => [x - 1, y + 1],}function getRandomProperty(obj) {const keys = Object.keys(obj)const randomKey = keys[Math.floor(Math.random() * keys.length)]return obj[randomKey]}return [getRandomProperty(directions)(point),getRandomProperty(directions)(point),]}const startingPoint = [25, 25]const movements = Array(3000).fill(null)const [, randomWalk] = mapAccum(moveRandomly, startingPoint, movements)
You can use the generated list to create a string that draws a line in an svg.
This
example uses JSX.
const pathD = randomWalk.reduce((acc, point) => `${acc} L ${point[0]} ${point[1]}`, 'M 25 25')return (<svg viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d={pathD} fill="transparent" stroke="black" stroke-width="0.1" /></svg>)