map/filter/reduce

A quick summary of reactivex.io/learnrx, the best material from Jafar Husain which totally changed the way I write javascript, especially dealing with async data sources like XHR / events / promises. Surprisingly, the same idea apply to sync data sources, too.

map

It’s a projection, a transformation which convert an array to another array with given function. In below case we try to get an new array which multiply each element from given array by 10.

[1, 2, 3].map(x => 10 * x);
// [10, 20, 30]
http://rxmarbles.com/#map

In real world we use map in many ways. The most popular one is probably to get a subset of array. ex: get names from a users collection.

const users = [ 
{ name: ‘Simon’, age: 33, gender: ‘male’ },
{ name: ‘Wendy’, age: 26, gender: ‘female’ },
{ name: ‘Candy’, age: 25, gender: ‘female’ }
];
users.map(user => user.name);
// ['Simon', 'Wendy', 'Candy'];

And for sure, we can augment the return value if needed, too.

users.map(user => user.name.toLowerCase() + '@fb.com');
// ['simon@fb.com', 'wendy@fb.com', 'candy@fb.com'];

filter

To validate each element in an array and return the valid elements only.

[2, 30, 22, 5, 60, 1].filter(x => x > 10);
// [30, 22, 60]
http://rxmarbles.com/#filter

With given above example, we can run a filter function to select all female from users

const users = [ 
{ name: ‘Simon’, age: 33, gender: ‘male’ },
{ name: ‘Wendy’, age: 26, gender: ‘female’ },
{ name: ‘Candy’, age: 25, gender: ‘female’ }
];
users.filter(user => 'female' === user.gender);
// [
// { name: ‘Wendy’, age: 26, gender: ‘female’ },
// { name: ‘Candy’, age: 25, gender: ‘female’ }
// ];

reduce

This is a bit more complex than map and filter, however, it turns out reduce is quite fundamental to flatten a collection. In this example the reduce function accumulates each value and return sum of all the values. like aforementioned, it flattens this array [1, 2, 3, 4, 5] to a single value 15.

[1, 2, 3, 4, 5].reduce((acc, curr) => acc + curr);
// acc: accumulated value
// curr: current value

Let’s demystify how reduce function works step by step. Starting from [round 2] acc stands for the sum of previous round (see bolded text).

[1, 2, 3, 4, 5].reduce((acc, curr) => acc + curr);
// [round 1] acc = 1 curr = 2 return 3 (acc of [round 2])
// [round 2] acc = 3 curr = 3 return 6 (acc of [round 3])
// [round 3] acc = 6 curr = 4 return 10 (acc of [round 4])
// [round 4] acc = 10 curr = 5 return 15

It might be easier to understand if we specify the initial value in 2nd parameter. In following example we can see that curr basically stands for each value in given array.

const initialValue = 0;
[1, 2, 3, 4, 5].reduce((acc, curr) => acc + curr, initialValue);
// [round 1] acc = 0 curr = 1 return 1 (acc of [round 2])
// [round 2] acc = 1 curr = 2 return 3 (acc of [round 3])
// [round 3] acc = 3 curr = 3 return 6 (acc of [round 4])
// [round 4] acc = 6 curr = 4 return 10 (acc of [round 5])
// [round 5] acc = 10 curr = 5 return 15

In a more practical example we can use reduce to compose a new function which aggregate given function(s).

const add1 = x => x+1;
const add3 = [add1, add1, add1].reduce((acc, curr) => {
return x => curr(acc(x))
});

Also, if we get a list of key-value pairs we can convert it to an object by reduce as well.

const citycodes = [
['94031', 'Palo Alto'],
['94043', 'Mountain View'],
['95014', 'Cupertino']
];
citycodes.reduce((acc, curr) => {
acc[curr[0]] = curr[1];
return acc;
}, {});
// {
// '94031': 'Palo Alto',
// '94043': 'Mountain View',
// '95014': 'Cupertino'
// }

In conclusion, map / filter / reduce can be used and combined in many places to perform very powerful calculation on collections. To learn more you could read reactivex.io/learnrx which has a lot of hidden gems and same concepts can be applied in not only array/collections, but also XHR / events / promises.