Underscore.js is a tiny JavaScript utility library that makes working with some of the common data structures used in JavaScript much easier. Minified and GZipped it weighs in at less than 4Kb and where possible it delegates functionality to native browser implementations for performance. It has no other dependencies and so adds very little overhead to your total script assets. It can be used on the client or server with equal ease.
Using Underscore is extremely easy; it isn’t tightly bound to the DOM, doesn’t make any assumptions about other libraries or frameworks that may be in use and doesn’t require any setup or configuration; you simply include the library file in your page and you can start calling its methods.
We’ll be using the following JavaScript arrays in the examples shown in this tutorial:
var myArray = ["one", "two", "three", "four", "five"];
var myArray2 = [1, 2, 3, 4, 5];
var myArray3 = [1, 5, 2, 2, 8, 5, 3, 8, 9, 0];
Working with arrays
Arrays are very common data structures in JavaScript, heavily used by many popular JavaScript libraries, for example, whenever jQuery returns an element (or elements) from the DOM, it returns it (or them) in an array. You may be using arrays and not even know it! In case anyone doesn’t know, an array is simply a collection of native values (strings, integers, objects, etc) where each value has a numerical index associated with it. Let’s look at some of the useful array methods provided by Underscore:
indexOf()
JavaScript has long had the indexOf() method for finding a character or sequence of characters within a string of text. The latest version of JavaScript (or ECMA Script upon which each browser’s implementation of JavaScript is based) includes an indexOf() method that works in a similar way but with arrays. It returns either the index of the first matching item in the array or -1 if a matching item is not found.
But guess what, only the very latest version of Internet Explorer supports this incredibly useful array method, so if older versions of IE must be supported, we need some other way of adding support. Enter the ring Underscore, which allows us to make use of it on older browsers.
To use this method to find the index of the string three in our test array, we could use the following code:
_.indexOf(myArray, "three")
All Underscore methods are attached the _ object, much in the same way that jQuery methods are attached to the $ object. To use indexOf() we supply the array to search as the first argument and the term we are searching for as the second. There is also a third argument which takes a Boolean indicating whether the array is already sorted, which runs a much faster search algorithm. Underscore also ships with a lastIndexOf() method which finds the last occurrence of something within an array. Pretty useful.
union()
Another useful method, this time with no native JavaScript counterpart to leverage, is the union() method. This method takes any number of arrays and returns a single array containing any items that appear in one or more of the original arrays. To see the union of two of our example arrays, we could use the following code:
_.union(myArray2, myArray3)
The returned array will contain the items in the order in which the source arrays are passed in.
uniq()
Another exceptionally useful method, similar in some respects to the union() method is uniq(). This method accepts a single array as an argument and returns an array containing only the unique items from the source array. To filter out the duplicates in our third example array, we could pass it to uniq() like this:
_.uniq(myArray3)
Just like the indexOf() method that we saw a moment ago, this method can accept a Boolean as the second argument which specifies whether the source array has already been sorted. If it has a much faster algorithm is used to weed out the duplicates.
This method uses the strict equality test (===) in its comparator function to determine whether two items are identical or not. In some situations, this may be more strict than we require, for example this !== This. To help alleviate issues such as this, we can pass a transformation function to this method as the third argument. If we were trying to make the method case-insensitive, we could use this:
_.uniq(myArray3, false, function(item) {
return item.toLowerCase();
});
The transformation function receives the ‘current’ item each time it is invoked (it will be invoked for each item in the source array) and should return the transformed item. In this case, we just convert the item to lowercase before returning it. No more casing issues.
zip()
The zip() method takes any number of input arrays and returns a number of arrays where the same items from the same indices or the source arrays have been merged. It sounds tricky in words, but when you see what happens you’ll understand immediately. To zip up the first and second of our test arrays for example, we would use the method in this way:
_.zip(myArray, myArray2)
When you see the output of this expression, you’ll see that the first array returned contains the items “one” and 1, the second array contains the items “two” and 2, etc.
In this example, both source arrays contain the same number of items, so the output is pleasingly neat. We can also pass arrays with differing numbers of items and the method will still work, but some of the arrays returned will have undefined values at some of the indices.
range()
The range() method is interesting because although it is used with arrays, it doesn’t accept any arrays as arguments. Instead it accepts integers, and uses them to return an array containing a sequence of numbers.
It can accept several arguments; the first argument, which is optional and defaults to 0, is the starting number. The second argument is the number to stop at, and the third argument is the step value. Again, this may be a tricky method to get your head around theoretically, but in this case, examples speak louder than words, so let’s take a look at a couple of variations.
If a single argument is provided, it is assumed to be the number to stop at and 0 is used as the starting number. 1 is used as the step:
_.range(3)
The output of this would be an array containing the values 0, 1 and 2 (remember, array indices are zero-based). If two arguments are provided they are assumed to be the starting and stopping number. 1 is used as the step:
_.range(0,3)
This would give the same result as the first example. To use a step value other than 1 we muist provide all three arguments:
_.range(0,30,10)
This now gives the result 0,10,20. We can provide a lower ending number than the starting number if we wish; in this case, the step value provided should be negative:
_.range(30, 0,-10)
In this case, the output is now 30,20,10. Although possibly not used as often as other method we have looked at, range() is nevertheless a useful method when a list of numbers between a particular range is needed.
Summary
As well as the methods we have looked at in this example, there are many other just as useful methods provided by Underscore, so it is well worth having a play with the library and seeing what other situations it may help you in. Join me in the next part of this tutorial where we’ll be looking at method related to collections (arrays or array-like structures).