Book Image

Learning Underscore.js

By : Alexandru Vasile Pop
Book Image

Learning Underscore.js

By: Alexandru Vasile Pop

Overview of this book

Table of Contents (14 chapters)
Learning Underscore.js
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Getting started with Underscore by example


The best way to introduce Underscore is with examples. We will target the same problem and solve it first using plain JavaScript that is ES5 compliant followed by a couple of Underscore-based solutions.

The ECMAScript 5 starter example

The examples presented in this chapter are hosted in web pages, and they can be executed by opening the index.html example file in your preferred web browser.

We start with a dataset that contains various bicycles and each one has a specific type. We want to process the dataset and get a count of bicycles for each bicycle type. The dataset is represented by an array of bicycle objects created using the object literal notation:

var bicycles = [{
      name: "A fast bike",
      type: "Road Bike"
    }, {
      name: "An even faster bike",
      type: "Road Bike"
    }, {
      name: "A springy bike",
      type: "Mountain Bike"
    }, {
      name: "A springier bike",
      type: "Mountain Bike"
    }, {
      name: "An all-terain bike",
      type: "Mountain Bike"
    }, {
      name: "A classy bike",
      type: "Urban Bike"
    }, {
      name: "A modern bike",
      type: "Urban Bike"
    }, {
      name: "A blue bike",
      type: "Children Bike"
    }, {
      name: "A pink bike",
      type: "Children Bike"
    }, {
      name: "A noisy bike",
      type: "Children Bike"
    }, {
      name: "A clown bike",
      type: "Children Bike"
    }];

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Next, we will define a function that processes the dataset and creates an array of objects that contains the count of bicycles for a specific bicycle type:

var getBicyclesCountPerType = function() {
  var bicyclesCountPerType = [];
  bicycles.forEach(function(bicycle) {
    var isExistingType = false;
    bicyclesCountPerType.forEach(function(typeCount) {
      if (typeCount.type === bicycle.type) {
        typeCount.count += 1;
        isExistingType = true;
      }
    });
    if (!isExistingType) {
      bicyclesCountPerType.push({
        type: bicycle.type,
        count: 1
      });
    }
  });
  return bicyclesCountPerType;
};

The first highlighted function forEach is ES5 specific and enumerates the values of an array while executing a function for each element in the array. The forEach function is defined against Array.prototype, so it will be available to any Array instance.

The final code snippet for the first example relies on jQuery to append the results for each bicycle type as HTML content:

$(document).ready(function() {
  var outputContent = "There are " + bicycles.length + " bicycles:"
  var results = getBicyclesCountPerType();
  results.forEach(function(typeCount) {
    outputContent += "<br />";
    outputContent += " - " + typeCount.count + " of type: " + typeCount.type;
  });

  $("#output").html(outputContent);
});

Notice how we use forEach again to enumerate the final results. You can explore the example in the starter-example-with-ECMAScript5 folder from the source code for this chapter. In order to run this example, you need to open the index.html file in your preferred web browser. The example can also be executed directly at http://bit.ly/1WLzHuS.

You can view the example output in this screenshot:

The Underscore find starter example

In the previous example, we used two-nested forEach calls, and this approach seems inefficient under closer scrutiny. You cannot break a forEach loop, which means that you cannot stop after you processed the specific elements that were targeted. The second nested forEach call only needs to process a target element from the list rather than iterating the full list every time. Fortunately, Underscore has a function called find that iterates an array and stops when an element matching a specific criteria is found.

To use Underscore, we need to add a reference to a hosted version of the library in the head element of the example web page:

<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>

In the first example, we changed the function getBicyclesCountPerType() to use the Underscore find function:

var getBicyclesCountPerType = function() {
  var bicyclesCountPerType = [];
  bicycles.forEach(function(bicycle) {
    var currentTypeCount = _.find(bicyclesCountPerType, function(typeCount){
      return typeCount.type === bicycle.type;
    });
    if (currentTypeCount) {
      currentTypeCount.count += 1;
    }
    else
    {
      bicyclesCountPerType.push({
        type: bicycle.type,
        count: 1
      });
    }
  });
  return bicyclesCountPerType;
};

The code is terser and more efficient thanks to the find function, while the rest of the example remains unchanged. You can explore the full example in the starter-example-with-underscore.find folder from the source code for this chapter or by running it directly at http://bit.ly/1U7dVO4.

All Underscore functions are usually accessed through the Underscore global object named _. This name can be changed if it conflicts with other global variable names, and we will explore how to do this in Chapter 4, Programming Paradigms with Underscore.js. We will stick with the default Underscore global object name for most of the examples.

The full signature of the find function is _.find(list, predicate, [context]). This function accepts an array or an object as its first parameter list followed by a function parameter predicate that will be executed against each element of the array or against each property value of the object passed as the first parameter. The second parameter function predicate accepts an array element or an object property value and should return a Boolean value. If the return value is true, the _.find() function will stop iterating and will return the array element or the property value from the current position. The last optional parameter context is rarely used in this book and will be covered later on when we discuss scopes and the value of the this object. The find function signature was briefly explored here as it is used extensively in Underscore and subsequently in the rest of the examples.

Note

In ES6, there is also a find function defined against Array.prototype that can be used as an alternative. We will discuss this function and other similar ECMAScript 6 functions in Chapter 6, Related Underscore.js Libraries and ECMAScript Standards.

The Underscore countBy starter example

The previous example introduced Underscore as an incremental improvement compared to the example before that. Among Underscore's 100+ functions, there is one that can replace the getBicyclesCountPerType() function completely and significantly reduce the number of lines of code. This function is countBy and its signature is _.countBy(list, iteratee, [context]). While its signature is similar with the one for the find function, the second function parameter iteratee is expected to return a string value representing a key for the iterated item. The key is used to group different elements of an array or different object property values depending on the list parameter type.

The countBy function returns an object with properties that have names taken from the keys supplied by the iteratee function. The value of such a property is the count of items from the list parameter that share the current property name as key.

The getBicyclesCountPerType() function invocation can be completely replaced with the _.countBy() function invocation:

var result = _.countBy(bicycles, function(bicycle){
    return bicycle.type;
});

The result object value has the following JSON representation:

{
    "Road Bike": 2,
    "Mountain Bike": 3,
    "Urban Bike": 2,
    "Children Bike": 4
}

The code tasked with displaying the example output needs to be changed accordingly. Instead of manipulating the array returned by the getBicyclesCountPerType() function it should manipulate the result object properties. We will use another Underscore function _.pairs(), which converts object properties to an array of elements where each element is a property name and value pair. Such a pair is a two-dimensional array itself, and you can see how it is declared and referenced in the highlighted sections from the following example:

$(document).ready(function() {
  var outputContent = "There are " + bicycles.length + " bicycles:"
  var result = _.countBy(bicycles, function(bicycle) {
    return bicycle.type;
  });
  _.pairs(result).forEach(function(typeCountPair) {
    var key = typeCountPair[0];
    var value = typeCountPair[1];
    outputContent += "<br />";
    outputContent += " - " + value + " of type: " + key;
  });
  $("#output").html(outputContent);
});

We have now dramatically reduced the size of the initial example by using Underscore functions while maintaining the same functionality. You can explore the full example in the starter-example-with-underscore.countBy folder from the source code for this chapter or by running it directly at http://bit.ly/1JdNwc6.

We started with one example that uses ES5 functions, followed by one that uses an Underscore function that is also an ES6 function. The final example uses another Underscore specific function that provides a higher level of data manipulation compared to built in JavaScript functions. You should find a lot more Underscore examples of this kind throughout the book.