From irritation to iteration #1

The definition of insanity is doing the same thing over and over without iterating over it

ES6 had introduced plenty of array iteration functions. Some had celebrated the comfort they gave, some wonder who needs sugar programming. And I looked what’s left for Underscore JS. Before adopting the new methods, I wondered that’s there cost. I started by comparing the good old loop, with the new forEach method, expecting the second one to be a little slower.

let arr = [];
for(let i = 0;i<10000000;i++){
   arr.push({value:Math.random()});
}

console.time('gooOldLoop');
for(let i=0;i<arr.length;i++){
   arr[i].value = arr[i].value+1;
}
console.timeEnd('gooOldLoop');
//gooOldLoop: 1003 ms

console.time('forEach');
arr.forEach(function(item, index, array) {
   item.value = item.value+1;
});
console.timeEnd('forEach');
//forEach: 306 ms

This was the last thing expected to find. The difference was so significant, that I needed a double check before I felt confident to announce that there is a knockout. But even if the forEach was just sugar programming, then the sugar-rush is doing something good.

Iteration methods

JavaScript presents thirteen iteration methods and most of them are working over the same principle. First, check the length of the array, then processing the array by passing the arguments functions over and over to the same function, usually with the same arguments.

//arr.XXXXX(callback(element[, index[, array]])[, thisArg]);
arr.forEach((element,index,orijinalArray) => {
   retun someValueToPassOn;
});

Few points worth mentioning:

A. Length

Unlike the loop, the length is calculated at the beginning. So If you add or remove an element, it will iterate the original length.

let arr = [1,2,3];
arr.forEach((item,index,arr) => {
   console.log(item);
   arr.push(index+arr.length);
});
// 1, 2, 3 
// arr ==> (6) [1,2,3,3,5,7]

for(let index=0; index < arr.length && index < 10; index++){
   console.log(arr[index]);
   arr.push(index+arr.length);
}
// 1, 2, 3, 3, 5, 7, 9, 11, 13, 15, 13
// arr ==> (13) [1,2,3,3,5,7,9,11,13,15,17,19,21]

Without the limit 10 conditions, there had been an endless loop.

B. Errors

In most case, error over one of the iteration will prevent the code from reaching the next callback. So the iteration will stop there, and there will be no final callback value.

let arr = [{b:{c:1}},{},{b:{c:2}}];
let arr2 = arr.filter((item,index) => {
   console.log(index);
   return a.b.c < 3;
});
// 0
// Uncaught TypeError: Cannot read property 'c' of undefined
// arr2 is not defined

C. Null value

In most cases, a null value in the array will be ignored.

[{b:{c:1}},,{b:{c:2}}].forEach((item,index) => {
   console.log(index);
   return item.b.c < 3;
});
// 0
// 2

But only in most, sometimes…

let arr = [{b:1},,{b:2}].find((item,index) => {
   console.log(index);
   return item.b.c > 3;
});
// 0
// 1
// Uncaught TypeError: Cannot read property 'b' of undefined
// arr == undefined

The plan was to continue and listing to all the different methods, but while doing so it became so late, that my alarm clock threatened to wake me up. So I’ll leave that for next time: From irritation to iteration #2.