/ javascript

Iterating data with Map, Reduce, ForEach and Filter


Loops are the important construct of the programming language. More powerful is the looping, more things you can achieve in less time.

Let's see how to iterate data with Map, Reduce ForEach and Filter.

Filter is not actually for iteration but a big help while iterating.

Map, Reduce and Filter are the looping constructs for Arrays, introduced in ES6.

Pure functions

Pure functions are the functions which do not have any side effect and for any specific input, they will always return same output.

Primary side effects of function are:

  • altering the input
  • writing to other globally available entities; entities can be
    • DOM
    • Network
    • Variables

Map

Map function is used to create a one to one mapping of a input/sample array. The result array is same number of items, i.e. the input and output array and equal .length properties.

Map function is a pure function.

For example:

// Generate 8 digit random number
// and convert to string
// and then split it to get each number as array item
const strArray = parseInt(Math.random() * 10000 * 10000, 10).toString().split('')

// lets convert each item to integer again
// (recommended if using native functions)
const randomNumArray1 = strArray.map(item => Number(item));
// or simply
const randomNumArray2 = strArray.map(Number);

So on executing, following may be the output:

["2", "3", "9", "2", "2", "6", "2", "0"] // strArray
[2, 3, 9, 2, 2, 6, 2, 0] // randomNumArray1
[2, 3, 9, 2, 2, 6, 2, 0] // randomNumArray2

Map function can accept two arguments. First of them is a function/callback. Let's call it a Mapper function. Second argument is the context, i.e. the this. The second argument is needed in very rare cases.

Mapper function can have maximum three arguments: currentItem, indexOfCurrentItem and the arrayBeingOperated.

Mapper function must return a value.

Detailed Reading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Filter

Filter function is used to filter out the items from an input array. It is also a pure function.

Filter accepts a callback argument which return Boolean output. And based on the output, the item is kept or removed from the array.

If the filterCallback returns false, item is rejected.

And if the filterCallback return true, the item is being added to new Array.

From the Map example, lets have the random array. And filter out the odd items, the output will only contain the even numbers.

const strArray = parseInt(Math.random() * 10000 * 10000, 10).toString().split('')
const randomNumArray = strArray.map(item => Number(item));
const evenNumbers = randomNumArray.filter((item) => item % 2 === 0);

And output will be:

// strArray
(8) ["9", "6", "5", "8", "4", "9", "4", "6"]
// randomNumArray
(8) [9, 6, 5, 8, 4, 9, 4, 6]
// evenNumbers
(5) [6, 8, 4, 4, 6]

And similarly filterCallback will be (item) => item % 2 !== 0 to get all odd numbers.

The filterCallback has same parameter signature as of Mapper function; currentItem, indexOfCurrentItem and the arrayBeingOperated.

Detailed Reading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Reduce

Reduce function is used to transform the input array to some other collection. The output collection can be anything, Array, Object, String, Number etc.

Reduce function is most powerful iterator as it can remove the need to iterate on the collection again and again. In one go you can do many operations on the current item.

map and filter can be achieved through reduce function.

Reduce function accepts two arguments, callback and the initialValue.

The callback or let's call it reducerFunction, can accept maximum four arguments, out of which first two are the key arguments and rest are optional.
The Arguments are
- accumulator: this is the resultant collection from previous iteration. For first iteration, the initialValue is used, if initialValue is not specified, the reducerFunction will consider first item of the input array as initialValue
- currentItem: the current item being processed from the inputArray
- indexOfCurrentItem: index of the current item being processed from the inputArray
- inputArray: the array being operated on

Reduce function starts with a initialValue and on every iteration, the reducerFunction will keep returning the newly formed collection accumulator

Let's see a simple example of achieving map function's capability be reduce function:

const strArray = parseInt(Math.random() * 10000 * 10000, 10).toString().split('')
console.log(strArray)
const randomNumArray = strArray.reduce((accumulator, item, index) => {
    console.log(index, item, accumulator);
    accumulator.push( Number(item) );
    console.log('new collection', accumulator);
    return accumulator;
}, []);

And following is the output of above function:

// strArray
(8) ["3", "0", "3", "9", "3", "9", "8", "2"]

//iteration 1
0 "3" []
new collection [3]

//iteration 2
1 "0" [3]
new collection (2) [3, 0]

//iteration 3
2 "3" (2) [3, 0]
new collection (3) [3, 0, 3]

//iteration 4
3 "9" (3) [3, 0, 3]
new collection (4) [3, 0, 3, 9]

//iteration 5
4 "3" (4) [3, 0, 3, 9]
new collection (5) [3, 0, 3, 9, 3]

//iteration 6
5 "9" (5) [3, 0, 3, 9, 3]
new collection (6) [3, 0, 3, 9, 3, 9]

//iteration 7
6 "8" (6) [3, 0, 3, 9, 3, 9]
new collection (7) [3, 0, 3, 9, 3, 9, 8]

//iteration 8
7 "2" (7) [3, 0, 3, 9, 3, 9, 8]
new collection (8) [3, 0, 3, 9, 3, 9, 8, 2]

// randomNumArray
(8) [3, 0, 3, 9, 3, 9, 8, 2]

Or let's make an Object out of an Array

const input = [123, 456, 789, 102, 345];
const output = input.reduce((accumulator, item, index) => {
    accumulator[`item-${index+1}`] = item;
    return accumulator;
}, {});
console.log(output);

which will give us:

{item-1: 123, item-2: 456, item-3: 789, item-4: 102, item-5: 345}

Detailed Reading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

Iterating on Objects

Iterating on object is very similar to iterating on the Arrays. The trick is to iterate over keys of Object and the individual key can be used to access specific item from object itself.

To iterate over keys, we need to have keys as Array which is easily done by Object.keys() function.

Lets see following example where we take output variable {'item-1': 123, 'item-2': 456, 'item-3': 789, 'item-4': 102, 'item-5': 345} from previous example and make a new object of every item being another object label and value:

const inObj = {'item-1': 123, 'item-2': 456, 'item-3': 789, 'item-4': 102, 'item-5': 345};

const outObj = Object.keys(inObj).reduce((accumulator, key, index) => {
    accumulator[`item-${index+1}`] = {
      label: `Item ${index+1}`,
      value: inObj[key],
    };
    return accumulator;
}, {});

console.log(outObj);

and output is:

{item-1: {…}, item-2: {…}, item-3: {…}, item-4: {…}, item-5: {…}}
    item-1: {label: "Item 1", value: 123}
    item-2: {label: "Item 2", value: 456}
    item-3: {label: "Item 3", value: 789}
    item-4: {label: "Item 4", value: 102}
    item-5: {label: "Item 5", value: 345}

forEach

forEach method on Array is an impure iterator function which accepts similar callback as for map.

Only difference is that input array will be modified. So .forEach won't return anything and the callback function is also not needed to return anything.

For example:

const strArray = parseInt(Math.random() * 10000 * 10000, 10).toString().split('')
const randomNumArray = strArray.forEach(item => Number(item));
console.log(strArray, randomNumArray);

Detailed Reading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

These above listed array functions will increase your productivity in your regular JavaScript tasks to a large extent.

Please provide yor valuable feedback/suggestions in the comments below.