Chaser Tech
News and views from the engineering team at Chaser

An introduction to Underscore.js

By Chiara Buzzi.

Today’s post is about Underscore.js, a wonderful library that I’ve discovered while working at Chaser. When I started using it I was really impressed by how useful and powerful it is. I recommend trying it out, especially if, like me, you’re just starting out as a developer.

Underscore is a JavaScript library that includes over one-hundred helper functions. These helpers can be used to perform a whole range of common operations that would otherwise be rather laborious and time-consuming to implement. For example, it includes functions that iterate over arrays, sort and shuffle collections, compare objects, and more.

In this post, I will include some examples of the functions that we use in CHASER.

Getting started with Underscore

There are several ways to install Underscore: you can use the npm command npm install underscore, install it with Bower (bower install underscore) or, if you use Meteor, run meteor add underscore.

On the official website you can find other ways to install the library, as well as download it directly. There is also an annotated version of the source code that is very easy to read and includes plenty of informative comments.

Functions we use in CHASER

We use Underscore quite extensively in CHASER. Below is a selection of some of the functions that we use:

_.extend()

As suggested by the name, this function can be used to extend objects. It can be very useful to extend an object only when a condition is satisfied, or to create a new object using an existing one as the starting point. We find it really useful for tweaking MongoDB queries and modifiers.

For example:

var organisation = {name: 'Chaser', country: 'UK', industry: 'Tech'};
var anotherOrganisation = _.extend({employees: 5}, organisation);

anotherOrganisation becomes:

{employees: 5, name: 'Chaser', country: 'UK', industry: 'Tech'}

When using _.extend(), it is important to pay attention to the order of the arguments. The first argument is the destination object, to which the properties of the source object (the second argument) are copied. The destination object is then returned.

_.has()

_.has() is a simple but handy function that checks if an object includes the specified field. It should be noted that _.has() returns true also when the value of a field is 0, null or an empty string.

For example:

var organisation = {name: 'newOrganisation', employees: 50, unpaidInvoices: 150};
var entrepreneur = {name: 'John Smith', age: 34, unpaidInvoices: 100};

_.has(organisation, 'employees') returns true, while _.has(entrepreneur, 'employees') returns false.

_.isEmpty()

_.isEmpty() is equally straightforward: it returns true if an object is empty and false if it’s not. _.isEmpty() returns true also when an object is null or undefined.

Here are some examples:

  • var organisation = {};
    _.isEmpty(organisation) returns true

  • var organisation = null;
    _.isEmpty(organisation) also returns true

  • var organisation;
    _.isEmpty(organisation) also returns true

_.pluck()

_.pluck() is a great alternative to map() and, similarly to its Ruby on Rails namesake, it can be used to create a new array by extracting specific values from a collection. Here is an example:

var organisations = [
  {name: 'newOrganisation', employees: 2, unpaidInvoices: 30},
  {name: 'oldOrganisation', employees: 30, unpaidInvoices: 120},
  {name: 'corporation', employees: 2000, unpaidInvoices: 76}
];

var employeesArray = _.pluck(organisations, 'employees');

employeesArray becomes [2, 30, 2000].

_.findWhere()

This function is particularly useful for searching through large collections; it returns the first item in the collection that matches the specified fields.

For example:

var organisations = [
  {name: 'newOrganisation', employees: 2, unpaidInvoices: true},
  {name: 'oldOrganisation', employees: 30, unpaidInvoices: true},
  {name: 'corporation', employees: 2000, unpaidInvoices: false}
];

_.findWhere(organisations, {employees: 30, unpaidInvoices: true});

returns:

{name: 'oldOrganisation', employees: 30, unpaidInvoices: true}.

As shown in the example above, _.findWhere() is powerful when used to search for a match to multiple fields.

This is of course only a very small selection of the functions provided by Underscore. I definitely recommend visiting the official website and browsing through the documentation. It may be that one of the functions available could be useful for one of your projects!

Underscore or plain JavaScript?

In some cases native JavaScript methods do just as well as Underscore. However, it’s important to keep in mind that one of the advantages of using a library like Underscore is cross-browser compatibility which, instead, is not always guaranteed when using some of the new native JavaScript methods.

Here are a couple of examples where Underscore functions and native JavaScript methods produce similar outcomes:

_.reduce() vs reduce()

_.reduce() iterates over an array, executes for each element the specified function, and reduces the array to a single value. It works similarly to the JavaScript reduce() method.

Here is an example:

var employeesArray = [1, 3, 5, 7];

function count(firstValue, secondValue) {
  return firstValue + secondValue;
}

Both _.reduce(employeesArray, count) and employeesArray.reduce(count) return 16.

One of the main differences is, however, that while Underscore ‘Collection’ functions, like _.reduce(), can be used for both objects and arrays, many native JavaScript methods can only be used for arrays.

Referring to the above example, we could do the following:

var employeesObject = {teamA: 1, teamB: 3, teamC: 5, teamD: 7};
_.reduce(employeesObject, count);

Here, _.reduce() would still return 16. However, the native JavaScript method reduce() would not work with employeesObject.

_.contains() vs includes()

The Underscore _.contains() method works very similarly to the native JavaScript method includes().

Using Underscore, _.contains(myArray, 5) checks if myArray includes the value 5. The same can be achieved with the ES2016 includes() method by writing: myArray.includes(5).

However, if the array was null, _.contains() would return false, while includes() would return an error.

Additionally, not all browsers support the new ES2016 method yet.

Alternatives to Underscore

Underscore is not the only library that provides utility functions. Amongst the most popular alternatives there are Lodash, that offers an even wider range of functions, and Ramda which, according to the official documentation, differentiates itself by emphasizing ‘a purer functional style’. For CHASER, our library of choice is currently Underscore; however, browsing the web you will find excellent reviews and tutorials on both Lodash and Ramda.

To finish off, we would like to thank Jeremy Ashkenas and all the other contributors to Underscore for developing and maintaining a library that is useful to so many developers.