JavaScript – forEach with Breaking

It seems that one of the top complaints about the Array.prototype.forEach() function is the fact that there is no easy way to break out of the loop. If you are one of those people that would like to see an implementation, here you go:

function forEach(arr, fn, thisObj) {
  // Try-catch surrounds the loop to allow throwing the array as an equivalent
  // for breaking out of the loop.
  try {
    for(var i = 0, len = arr.length; i < len; i++) {
      fn.call(thisObj, arr[i], i, arr);
    }
  }
  catch(e) {
    // If the error thrown was not the array, allow the error to continue up the
    // stack.
    if(e !== arr) {
      throw e;
    }
  }
  
  // Return a reference to the array to all for further manipulation.
  return arr;
}

Calling this function would be similar to using Array.prototype.forEach.call() to call the forEach() function introduced in JavaScript 1.6. Here is an example call will break out of the loop as soon a perfect square is found:

var perfectSquare;
forEach([2,3,5,6,7,8,9,10,16], function(num, index, arr) {
  var sqrt = Math.sqrt(num);
  if(sqrt == parseInt(sqrt, 10)) {
    perfectSquare = num;
    throw arr;
  }
});

alert(perfectSquare);  // first perfect square found

For proof that this actually works, you can check out this JSBin page.

As you see in the code, in order to break out of the loop, you will need to throw the array that you are traversing. As you may have guessed, you can also fake the continue action by simply using return.

JavaScript – Add Commas To Numbers

At times, I have had to add commas to numbers in order to make them look better in different applications. Even though there isn’t a native way to do this in JavaScript, the code can be quite short, sweet and to the point:

function addCommas(intNum) {
  return (intNum + '').replace(/(\d)(?=(\d{3})+$)/g, '$1,');
}

The downside of the above function is that it will only work for integers. Here is a function that will get the job done for one number or a string which contains one or more numbers:

function delimitNumbers(str) {
  return (str + "").replace(/\b(\d+)((\.\d+)*)\b/g, function(a, b, c) {
    return (b.charAt(0) > 0 && !(c || ".").lastIndexOf(".") ? b.replace(/(\d)(?=(\d{3})+$)/g, "$1,") : b) + c;
  });
}

First this function forces the parameter that was passed in to become a string. Next, a global replace of a regular expression looking for a four-digit or longer number goes through and inserts the commas in the correct place. After that, the result is returned as a string.

Here are some tests that I ran on the function to make sure that it works correctly:

// Delimit a random number.
alert("Random number:  " + delimitNumbers(Math.random() * (1 << 30)));

// Delimit all powers of 2 (from 2^0 to 2^30) in a string.
var powersOf2 = Array.range(31).map(function(i){return 1 << i});
alert("Powers of 2:\n" + delimitNumbers(powersOf2.join("\n")));

One thing you want to keep in mind if you choose to test the delimitNumbers() function with the above code is the fact that you will need to make sure that Array.range() and Array.prototype.map() are correctly defined. I did my test in the ScriptBox which always uses the newest version of jPaq (currently an alpha of v2.0).

Deep Copy of Arrays

With jPaq and many other JavaScript libraries, it is very easy to do a shallow copy of an array. On the other hand, it isn’t as easy to do a deep copy of arrays. For this reason, I plan on replacing the Array.prototype.clone() function with the following:

Array.prototype.clone = function(doDeepCopy) {
    if(doDeepCopy) {
        var encountered = [{
            a : this,
            b : []
        }];

        var item,
            levels = [{a:this, b:encountered[0].b, i:0}],
            level = 0,
            i = 0,
            len = this.length;

        while(i < len) {
            item = levels[level].a[i];
            if(Object.prototype.toString.call(item) === "[object Array]") {
                for(var j = encountered.length - 1; j >= 0; j--) {
                    if(encountered[j].a === item) {
                        levels[level].b.push(encountered[j].b);
                        break;
                    }
                }
                if(j < 0) {
                    encountered.push(j = {
                        a : item,
                        b : []
                    });
                    levels[level].b.push(j.b);
                    levels[level].i = i + 1;
                    levels[++level] = {a:item, b:j.b, i:0};
                    i = -1;
                    len = item.length;
                }
            }
            else {
                levels[level].b.push(item);
            }

            if(++i == len && level > 0) {
                levels.pop();
                i = levels[--level].i;
                len = levels[level].a.length;
            }
        }

        return encountered[0].b;
    }
    else {
        return this.slice(0);
    }
};

The above code can be used to make deep copies of arrays. It is important to note, though, that only sub-arrays are deeply copied, not objects. The reason so much code is used is to even make it possible to make a deep copy of a recursive array structure. The following code exemplifies how to use this function:

// Create a recursive array to prove that the cloning function can handle it.
var arrOriginal = [1,2,3];
arrOriginal.push(arrOriginal);

// Make a shallow copy of the recursive array.
var arrShallowCopy = arrOriginal.clone();

// Prove that the shallow copy isn't the same as a deep copy by showing that
// arrShallowCopy contains arrOriginal.
alert("It is " + (arrShallowCopy[3] === arrOriginal)
    + " that arrShallowCopy contains arrOriginal.");

// Make a deep copy of the recursive array.
var arrDeepCopy = arrOriginal.clone(true);

// Prove that the deep copy really works by showing that the original array is
// not the fourth item in arrDeepCopy but that this new array is.
alert("It is "
    + (arrDeepCopy[3] !== arrOriginal && arrDeepCopy === arrDeepCopy[3])
    + " that arrDeepCopy contains itself and not arrOriginal.");

If you want to use this code right now, there is nothing stopping you. The code doesn’t require any library references and is fully cross-browser compatible. A JSBin example can be found here. It even works in JScript. The only thing that I ask is that you give credit where it is due. šŸ˜€