Skip to content
On this page

Map/Reduce

If you've read Google's famous paper "MapReduce: Simplified Data Processing on Large Clusters," you will have a general understanding of the concept of map/reduce.

Map

For example, if we have a function :

f(x)=(x2)

and we want to apply this function to an array [1, 2, 3, 4, 5, 6, 7, 8, 9], we can use map as follows:

javascript
f(x) = x * x



┌───┬───┬───┬───┼───┬───┬───┬───┐
│   │   │   │   │   │   │   │   │
▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼

[ 1   2   3   4   5   6   7   8   9 ]

│   │   │   │   │   │   │   │   │
│   │   │   │   │   │   │   │   │
▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼

[ 1   4   9  16  25  36  49  64  81 ]

Since the map() method is defined in JavaScript's Array, we call the Array's map() method and pass our own function to get a new Array as a result:

javascript
function pow(x) {
    return x * x;
}

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let results = arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
console.log(results);

Note: The argument passed to map() is pow, which is the function object itself.

You might think that we don't need map(), as we can write a loop to calculate the result:

javascript
let f = function (x) {
    return x * x;
};

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let result = [];
for (let i = 0; i < arr.length; i++) {
    result.push(f(arr[i]));
}

This is indeed possible, but from the loop code above, we cannot immediately see "apply f(x) to each element of the Array and generate a new Array of results."

Thus, map(), as a higher-order function, abstracts the operation rules, allowing us not only to compute simple

f(x)=x2

but also to calculate any complex function, for example, converting all numbers in an Array to strings:

javascript
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']

Just one line of code.

Reduce

Now let's look at how reduce works. The reduce() method on an Array applies a function to the Array's ([x1, x2, x3...]). This function must take two parameters; reduce() continues accumulating the result with the next element in the sequence, effectively performing:

[x1,x2,x3,x4].reduce(f)=f(f(f(x1,x2),x3),x4)

For instance, to sum an Array, we can use reduce as follows:

javascript
let arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
    return x + y;
}); // 25

If the array has only one element, an additional initial parameter is needed to ensure there are at least two:

javascript
let arr = [123];
arr.reduce(function (x, y) {
    return x + y;
}, 0); // 123

Exercise: Use reduce() to calculate the product:

javascript
function product(arr) {
    // FIXME:
    return 0;
}

// Test:
if (product([1, 2, 3, 4]) === 24 && product([0, 1, 2]) === 0 && product([99, 88, 77, 66]) === 44274384) {
    console.log('Test passed!');
}
else {
    console.log('Test failed!');
}

To transform [1, 3, 5, 7, 9] into the integer 13579, reduce() can also be useful:

javascript
let arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
    return x * 10 + y;
}); // 13579

If we continue to improve this example, we can figure out how to convert a string 13579 into an Array—[1, 3, 5, 7, 9]—and then use reduce() to write a function that converts a string to a Number.

Exercise: Without using JavaScript's built-in parseInt() function, implement a string2int() function using map and reduce:

javascript
function string2int(s) {
    // FIXME:
    return 0;
}

// Test:
if (string2int('0') === 0 && string2int('12345') === 12345 && string2int('12300') === 12300) {
    if (string2int.toString().indexOf('parseInt') !== -1) {
        console.log('Do not use parseInt()!');
    } else if (string2int.toString().indexOf('Number') !== -1) {
        console.log('Do not use Number()!');
    } else {
        console.log('Test passed!');
    }
}
else {
    console.log('Test failed!');
}

Exercise

Please normalize user input of improperly formatted English names to capitalized first letters and lowercase for the rest. Input: ['adam', 'LISA', 'barT'], Output: ['Adam', 'Lisa', 'Bart'].

javascript
function normalize(arr) {
    // FIXME:
    return [];
}

// Test:
if (normalize(['adam', 'LISA', 'barT']).toString() === ['Adam', 'Lisa', 'Bart'].toString()) {
    console.log('Test passed!');
}
else {
    console.log('Test failed!');
}

Little Ming hopes to use map() to convert strings to integers, and his code is quite concise:

javascript
let arr = ['1', '2', '3'];
let r;

r = arr.map(parseInt);
console.log(r);

However, the result is 1, NaN, NaN. Little Ming is puzzled. Please help him find the reason and correct the code.

Hint: Refer to the documentation for Array.prototype.map().

Map/Reduce has loaded