Functional Programming With Javascript: Reduce, Curry

Woohyun Jang
The Startup
Published in
3 min readNov 26, 2020

Until now, we made some pure util functions(ex. map, filter).

Today we’ll be going to make three functions, ‘reduce’ and ‘curry’. They’ll be good tools in the next step, pipelining.

1. Reduce

Reduce function takes the collection, iteratee, and accumulator(optional, initial value). And it returns just one accumulated result of running each element in collection thru iteratee.

Accumulator, it’s the 3rd parameter is an optional value. If there's no, the first element of the collection will be the initial value.

The iteratee is called each element of the collection. It takes the previous iteratee’s result and current element and returns a new result.

const usersMock = [  { id: 1, name: 'user1', age: 11 },  { id: 2, name: 'user2', age: 21 },  { id: 3, name: 'user3', age: 23 },  { id: 4, name: 'user4', age: 11 },  { id: 5, name: 'user5', age: 37 },  { id: 6, name: 'user6', age: 23 },];
test('reduce', () => { expect(reduce(usersMock, (sum, user) => sum + user.age, 0)).toBe(126);});

In this code, reduce returns the sum of users’ age. The iteratee function gets the initial value 0, and add the first user’s age. If the first cycle is ended, the second cycle starts with the first result and the second element. Repeat this process until the end. Then, how can we implement the ‘reduce’? It also traverses the collection, so let’s use the ‘each’.

const each = require('../each');function reduce(collection, iteratee, accumulator) {  let iterable = collection;  let result = accumulator;  if (arguments.length == 2) {    result = collection[0];    iterable = Array.prototype.slice.call(collection, 1);  }  each(iterable, (value) => {    result = iteratee(result, value);  });  return result;}module.exports = reduce;

We take 2 or 3 arguments. If there are 2, we use the first element as the third argument. And traversing the ‘each’ loop, set the result to iteratee’s return value.

Map, Filter, Reduce

All most data processings are consist of these three functions, map, filter and reduce.

The ‘map’ matches data one-to-one. It changes each value of data so It returns the same length collection.

The ‘filter’ filters the data according to condition. There is no change in each value. But the length of data can be changed.

Finally, the ‘reduce’ returns just one value. It brings all the elements together to create one result.

2. Curry, Curry-right

Curry is a very interesting function. It takes a function and returns a new function. Curiously, it makes an input function to take input sequentially!

Let’s see the code first.

function curry(fn) {  return (first, ...args) => args.length
? fn(first, ...args)
: (...args2) => fn(first, ...args2);
}module.exports = curry;

‘curry’ returns a new function. If the new function takes just one input parameter, it returns a function with the first input set. So we can use it when we need it. How can we use it? Think about this code.

test('curry', () => {  const curryReduce = curry(reduce);  const numbersReduce = curryReduce([1, 2, 3, 4, 5]);  const sumTotal = numbersReduce((sum, value) => sum + value, 0 );  const sumOdds = numbersReduce((sum, value) => value % 2 ? sum + value : sum, 0 );  expect(sumTotal).toBe(15);  expect(sumOdds).toBe(9);});

We called the ‘curry’ with ‘reduce’ as input. ‘reduce’ takes only 2 or 3 arguments, But the curryReduce now can take just a first argument. As a result, ‘numbersReduce’ function was created. we can put any iteratee and accumulator!

We can also make ‘curryr’ function. It makes an input function can take just a second argument first.

function curryr(fn) {  return (first, ...args) => args.length
? fn(first, ...args)
: (innerFirst, ...args2) => fn(innerFirst, first, ...args2);
}module.exports = curryr;

So in ‘reduce’ case, we can put the iteratee logic first.

test('curryr', () => {  const curryrReduce = curryr(reduce);  const sumReduce = curryrReduce((sum, value) => sum + value);  const sumTotal = sumReduce([1, 2, 3, 4, 5]);  expect(sumTotal).toBe(15);});

As you can see from the future, we’ll usually put the iteratee logic first, and enter the data later like that.
Now that the ingredients are all ready, let’s start pipelining using functions in next time!

Github Source Code

--

--