Hey reader! First of all a happy happy new year! Wishing you happiness, prosperity, good health, and success in the year ahead! \:D/
For this blog post, we’ll be looking into Underscore.JS. As described in the Underscore.JS page, Underscore is
“a JavaScript library that provides a whole mess of useful functional programming helpers without extending any built-in objects.”.
In a bird’s eye view, functional programming is the use of functions to transform values to other structures or into units of abstraction (and indeed Underscore provides a battalion of functions that you can use as is or use inside other functions).
Most blog posts say that if you miss the usual functional methods (available in most functional programming languages like Ruby) in your Javascript code then, Underscore is for you!
Looking through the available functions and sample code, indeed, it’s true! Underscore provides a wide array of functions including pluck, map, and reduce which we see (and use) most of the time with our server code and other functional programming languages. With these, it is much easier to manipulate and transform data even on the client side.
To see a quick sample:
Let’s say we have this array:
var ratings = [80, 90, 99, 98, 100, 60, 50, 70];
And we want to add 5 to each of the array elements. With pure JavaScript, we do this:
for(i = 0; i < ratings.length; i++) { ratings[i] = ratings[i] + 5; }
But with Underscore on, we can do a simple one-liner:
ratings = _.map(ratings, function(rating) { return rating + 5;});
Tadaaaa! Sweet and simple isn’t it? So now let’s get it going with Underscore.js.
Including Underscore.JS in our File
To utilize Underscore, we need to first load it in our page. There are many methods to achieve this depending on your need, you can have bower, npm (Node Package Manager), Require.JS, among others. For this blog post, I downloaded underscore.js and included it in our page.
http://underscore.min.js
We can check that is indeed loaded, by typing “_” in our console:
Now that we have Underscore is loaded, we can start coding either by typing in the browser console or adding JavaScript code to in our html file.
Getting Started with Array, Collections, and Objects
As you may have observed, all of Underscore’s functions can be invoked on “_” (but we could also change this as we’ll see later on :D)
On the left side of Underscore’s page, you can see the multitude of functions available in the different categories of Collections, Array, Functions, Objects, Utility, and Chaining.
In this blog post, we’ll see a few examples from those categories and we’ll see how we can combine them to form more complex queries.
For the examples below, this is the dataset that we will be working on:
_.each
We can use _.each to iterate over an objects (that can be enumerated)
For example we want print the items and the category where they belong:
_.each(grocery, function(item) { console.log(item.name + " belongs to " + item.category) });
_.pluck
Similar to Ruby’s pluck, we can use pluck when we want to extract a list of values for the given key (think of it like a shortcut to map when just getting the values).
For example, if we want to get all the names of the items in our grocery list, we could do:
_.pluck(grocery, 'name')
_.filter
Returns the elements in the list that returns true for the given condition.
Say we want to get all grocery items with price less than 50, we can have:
_.filter(grocery, function(item) { return item.price < 50; })
* _.filter is now the preferred name for _.select as mentioned in the official documentation but using _.select still works so far (you might see this often in other Underscore.JS blogs and tutorial). 🙂
_.defaults
In case there are some undefined properties, _.defaults fills them up for you depending on the default values that you provide.
grocery = _.map(grocery, function(item) { return _.defaults(item, {name: 'Unnamed', category: 'Uncategorized', subcategory: 'Unsubcategorized'}); });
On our remote controlled car object (last item in the list), we can see that after we ran _.defaults, category and subcategory were filled in and were set to Uncategorized and Unsubcategorized repectively.
_.pick
In an object’s sea of attributes, when you only want certain attributes, you can use _.pick.
Let’s say we want our array grocery to have objects that only include name and category, we can do:
console.log(_.map(grocery, function(item) { return _.pick(item, 'name', 'category'); }));
_.reduce
The reduce function allow us to compress a list to a single value.
For example, we want to add all the prices of our items. In our callback, we first pass acc, our accumulator and item, the variable upon which the elements of the list will be assigned to in the loop.
_.reduce(grocery, function(acc, item) { return acc + item.price }, 0);
_.groupBy
_.groupBy groups the list of a list into categories and returns an object with the categories as keys and with arrays containing the items that fit into that certain category.
_.groupBy(grocery, function(item) { return item.category })
_.countBy
_.countBy on the other hand, groups a list in categories that you can specify and returns how many belonged to the specific category.
Here, we counted how many belonged to the different price ranges, 1-30, 31-60, 60-100, and 100 and above.
_.countBy(grocery, function(item) { if (item.price < 30) return '1 - 30'; if (item.price < 60) return '31-60'; if (item.price < 100) return '60-100'; return '> 100'; });
_.range
_.range, on the other hand, is a handy function that gives the values within the given range (start inclusive, end exclusive) by increments of the given third parameter.
_.map(_.range(1, 12, 1), function(day) { return 'Day ' + day + ' of Christmas!'});
Doing _.range(0, 10, 2), on the other hand, gives 0, 2, 4, 6, 8. And having a negative incrementor like _.range(99, 0, -1) gives the numbers from 99 to 1 in descending order.
Continuing with Function and other Utility Functions
_.once
Another handy function from Underscore is _.once, it allows the wrapped function to be ran only once. Succeeding calls to the wrapped function just returns the given previous value.
generateRaffleCoupon = function() { // Assigns only one unique raffle number per user. var points = _.random(10); return points } a = _.once(generateUniqueId)
In the above example, only one raffle number can be assigned to a user and succeeding calls to our simple raffle number generator just gives the already assigned number.
The original unwrapped function, generateUniqueId(), on the other hand, just behaves normally and can gives different values for different calls as we can see in the last two calls.
_.times
_.times invokes the given function for the given number of times.
_.times(5, function(i) { console.log('hi ' + i)});
Method Chaining
Above, we saw a lot of handy functions that you should let you go up and running (but of course there are still so much more that you should also check out the official documentation :D)
As we mentioned above, functional programming involves the use of functions to accomplish a certain task, either to transform objects or to simplify and reduce an object or list to a single value. And in accomplishing a task, we may use not only one, but a series of functions to get our desired result. For this purpose, we can do method chaining.
Calling _.chain returns wrapped objects so we can only get the value until we call .value() on our chain.
console.log(_.chain(grocery).map(function(item) { return item.category + ' Product'; }).uniq().value());
Underscore.JS as a Templating Engine
Say we have a dynamic page that should say “Hello <name of user here>“. We need to have our html code evaluate a dynamic value that will be passed to it. For this purpose, underscore’s _.template comes handy.
So how do we do it?
First, let us declare our template and enclose the dynamic values with the default interpolation delimiter of Underscore <%= %>.
var template = 'Hello <%= name %>! Welcome to this site!';
And then let’s compile it with _.template,
var finalTemplate = _.template(template);
which returns as with a function that we can call.
console.log(finalTemplate({name: 'John Doe'}));
We now have our html code with the name variable replaced by the given parameter. To use this, what we can do is just assigned result ot html (for ease of use, I also used jQuery for DOM manipulation):
result = finalTemplate({name: 'John Doe'}); $('#header-txt').html(result);
Other Configurations
As mentioned above, there are several customizations that you can do with Underscore and here are some of them:
I. Changing _
We can change _ to other characters or set of characters that you like (maybe you want to do this if you have other use for the underscore character like storage for the last value), you can do the following:
var uscore = _.noConflict();
So now, we can use uscore instead of _
events = ['New Year', 'Graduation', 'Birthday', 'Anniversary'];
console.log(uscore.each(events, function(event) { console.log('Happy ' + event + '!!!')}));
But bear in mind that when you do this, _ no longer is usable, unless you re-assign.
II. Object Orientedness
scores = {'Bob': 1, 'Alice': 4, 'Mallory': 3}; (scores).keys(); (scores).invert(); (scores).keys().map(function(p) { return "Hi " + p + "!"; })
When doing this object oriented style, of using Underscore.JS, you can immediately chain functions without using _.chain.
III. Customized Template Delimiters
In case we want to change the delimeters from being <% %> to something else of our liking, we can do so by proving custom interpolation and evaluation regular expressions parameters to _.templateSettings.
In this example, we changed <%= %> to {{ }}
_.templateSettings = { interpolate: /\{\{(.+?)\}\}/g, evaluate: /\<\%(.+?)\%\>/g };
Sample application
So now, we try to build a sample application that uses Underscore.JS for data processing as well as a templating engine.
Say we have a product inventory, where our list of grocery items is displayed plus the user can also add more items.
The full source code can be found in this gist.
In implementing this, we used the following:
-
Use of Defaults
We allow empty categories and subcategories in our form so we need to fill them up with defagrocery = _.map(grocery,
function(item) { return _.defaults(item, {name: 'Unnamed', category: 'Uncategorized', subcategory: 'Unsubcategorized'}); });
-
Use of Templates
We also used templates to display dynamically our table. We call the updateView() function when the page loads and every time an item is added to our grocery list.
-
Customized Template Delimiters
We changed the interpolation delimiters from <%= %> to {{ }} and retained the evaluation delimiters as <% %>.
-
Some Functional Programming
We standardized our error and warning messages by creating the fail and warn methods which we can call anywhere accordingly.
We also created a parser for our input which is called before we push our the new item to our grocery list. This parser calls our fail and warn functions.
Demo:
There, as we saw, Underscore.JS is very helful and provides tons of functions that we can use to manipulate our data on the client side. With a mere 4 KB file size, it can already do wonders! Try it now! 🙂
References: