Introduction

Around the beginning of (programming) time, the for loop was created. It was a really good invention. It provided a way to iterate over a range of elements in an array and perform an action on those elements. For example, a rudimentary approach to finding the max value in a array might look something like:

var maxValue = 0;
for(var i = 0; i < arr.length; i++){
   if(arr[i] > maxValue){
      maxValue = arr[i];
   }
}

This accomplished the goal and did so in a standard way that was easily recognizable for most developers. Over time, a new function came into existence, that function was the forEach. This made it easier to traverse a collection of elements by:

  1. Removing the need to know exactly how many elements were in the collection
  2. Eliminating any “off-by-one” errors

The new way we’d find the max value would look something like:

var maxValue = 0;
forEach(var a in arr){
    if(a > maxValue){
       maxValue = a;
    }
}

That’s a little bit better, but when it comes to code readability, it’s not really measurably better.

Wouldn’t it be great if we could do something like:

var arr = [1,3, 5, 2, 7, 3];
var maxValue = arr.max();

We can accomplish this with the Lodash library.

What is lodash

Pulling straight from their site, lodash is “A JavaScript utility library delivering consistency, modularity, performance, & extras.”

That sounds nice, but what does it provide you as a developer? It provides an entire library of functions to simplify tasks in JavaScript. For example, have you ever wanted to find the words in a string? How would you normally do that? Probably by finding the regex to match the words and then apply that. If you’re like me, and you don’t mess with regexs all that often, that means looking up the syntax each time, or trying to find an example that is close that you can modify. With lodash, you don’t have to do that, you could simply use their words function. It would look something like this _('Angular, Backbone, & Ember').words() and with that, you’d get an array of the words ['Angular', 'Backbone', 'Ember'].

Lodash provides a lot of nice utility functions. Not just for arrays, but for strings, for objects and even some general JavaScript language utilities.

But we’re not going to look at all the functionality it provides. Instead, we’re going to talk about how using lodash can help us have cleaner, more readable code.

lodash Syntax

Before we start looking at how lodash can help make our code cleaner, let’s talk about a couple concepts around lodash’s syntax.

Lodash provides two ways of invoking its functions. The first looks like this: _.words(). With this syntax, the string you want to find the words for is passed in like _.words('my string'). There’s another syntax, that we used above, that allows us to wrap an item with lodash and then hang functions off the object. To do this, we place the element we’ll be performing our functions on inside _(). As we saw above, this looks like _('my string').words(). When we do this second syntax, there are some functions that need a .value() after the end to perform the calculation. For example, _('my string').words().value(). This allows us to chain several functions together before we execute the final result, but we’re getting ahead of ourselves.

A Real World Example

The first example is something that a lot of us have probably done in our lives. We have an array of objects and we need to check that each object matches a precondition before we continue. As a concrete example, perhaps we have an array of orders, and we want to make sure they’ve all shipped before we bill for them. To do this, we’re going to start by using lodash’s forEach function:

function areAllShipped(orders){
    var shipped = true;

    _(orders).forEach(function(order){
        if(!order.shipped){
            shipped = false;
        }
    });

    return shipped;
}

After grasping the lodash syntax, I’m confident most of us could realize the outcome: we’re assuming all of our orders have shipped, and we’ll only set that value to false once we find an order that hasn’t shipped.

But unless you know what the line of code is doing before you start looking at it, you’re probably not going to glance at the forEach and instantly see that it’s finding out if they’re all shipped (assuming that this line is not in a well named function, as ours is.)

So how can we make that more readable? We could use lodash’s all or every function. In reality, they’re the same function, but all is an alias to every. What would that code look like?

function areAllShipped(orders){
    return  _(orders).all({shipped: true}).value();
}

Right away we can see this function is much smaller, it’s 1 line instead of 8 lines. But the fact that it’s smaller doesn’t necessarily make it easier to read. However, as we look at what is being called we can see that it’s calling the all function on orders. Right away that tells us that it’s checking to see if a condition is met for every element in the array. Next we see a typical JSON notation to tell us what it’s checking. In this case, it’s checking that all orders have the property shipped set to true.

Chaining Methods

Having functions named things like all or some right away makes our code more readable. But what if we need to do something besides check if every order was shipped? What if we need to do a more complex calculation? Or what if we need to take several steps to get where we’re going? I think a lot of us have written a function that might look something like this:

_(events).each(function(event){
    var rating = 0;
    var commentLength = 0;
    _(event.ratings).each(function(r){
        if(r.rating > rating){
            if(r.description.length > commentLength){
                rating = r.rating;
                commentLength = r.description.length;
                event.comment = r.description;
            }
        };
    }).value();
}).value();

Without providing any context it’s not immediately obvious what this code is doing. We can notice that we’re doing a loop inside of a loop. Specifically, we’re iterating over all of our events and then inside each event we’re iterating over all of our ratings. Once we’re inside that, we’re looking to see if our rating is the highest rating, and then we try to find the longest comment for those ratings.

This was pulled from a sample project I use to show TDD; it’s a Yelp style review application. You can give an event a rating (1 to 5) and leave a comment as well. This function will be used to highlight the best rating with the longest description. That is, if two people both give the State Fair a 5 and one says “A+” and the other writes a few sentences about why it’s a 5, we want to highlight the second comment.

But, again, that wasn’t readily apparent. We had to walk through each line of code keeping track of how we got to that point. When I see code like this my thought process sounds something like:

  • Ok, so we’ve got an event
  • Now we’ve initialized some variables that I guess we’ll use later in a calculation…
  • Now each event has several ratings, and I’m walking through that list
  • Ok, I want to find the highest rated rating.
  • Once I’ve found that I’m comparing description length, and I’m checking that my length is longer than the previous one
  • Then I set all my values and exit out.
  • Ok, so I’m getting the highest rated, longest comment. Got it.

By contrast, when we start chaining methods together from lodash, what we wind up with looks more like this:

_(events).each(setDescriptionOfHighestRating).value();

function setDescriptionOfHighestRating(e){
    e.description = _(e.ratings).chain()
        .sortBy(['rating', 'description.length'])
        .reverse()
        .first()
        .get('description')
        .value();
}

Now when I walk through this code, my thought process is:

  • Ok, we’re sorting our arrays first by rating and then by description length.
  • Then we reverse the array. Oh, the sortBy sorts ASC by default.
  • Then I take the first one, which would be the highest rating
  • Then I get the description from that rating.

I need fewer thoughts to process what is going on, and I can actually see on a line-by-line basis what is going on. Instead of doing:

.sortBy(['rating', 'description.length'])
.reverse()

I could have also done

.sortByOrder(['rating', 'description.length'], [false, false])

where the second array in sortByOrder specifies whether it should be ASC (true) or DESC (false). However, I thought the first bit of code was a touch more readable, at least until you’re familiar with lodash.

Chain

Notice that right after I initialized the lodash object _(e.ratings) I called the chain() command. This tells lodash that I’m going to be chaining several commands together, and that it should wait until I tell it to give me the value. This is not required for all chains. However, there are some functions that by default end a chain. In our list those are first and get. If I had not used chain then when I called first() I would have received my entire rating object. Which would have been fine, because I could have easily grabbed the description property off of that object, but I wanted to use lodash’s get function after I had called first so I needed to call chain. Additionally, because I had called chain my call to get('description') returned a lodash object instead of a string, like it normally would have, so I had to call .value() at the end to evaluate this code.

Evaluation

By chaining these commands, lodash did not evaluate each step and store my result in an intermediary step. It waited until I called value() and then processed the entire expression.

Why do we Care About Readability?

Our second function is more readable, but does that really matter? Look back at the block of code where we have nested each blocks:

_(events).each(function(e){
    var rating = 0;
    var commentLength = 0;
    _(e.ratings).each(function(r){
        if(r.rating > rating){
            if(r.description.length > commentLength){
                rating = r.rating;
                commentLength = r.description.length;
                e.comment = r.description;
            }
        };
    }).value();
}).value();

Do you see the bug here? I’d like to say that I put this bug here deliberately, but I didn’t. As I said earlier, this is from a sample project that I use to teach some of the basics of TDD. And what was interesting to me was that this block was written without TDD, and I only found the bug after I wrote the same block using TDD (but that’s a topic for another blog!) The bug occurs once we start iterating over each rating. Let’s assume we have an array that looks like this:

[{
    rating: 1,
    description: 'Horrible'
}, {
    rating: 3,
    description: 'It was so-so. Not that great, but not horrible, either.'
},{
    rating: 5,
    description: 'It was pretty neat.'
}, {
    rating: 5,
    description: 'This was an amazing year for the State Fair.'
}]

When we start iterating through our list, our first rating is 1, so we set that to the max value, and then we set the length of our description to be the comment. After all, ‘Horrible’ is longer than nothing, which is what we had.

Next, we come to our rating of 3 with a longer comment. Since 3 is greater than 1, we assign our new rating (3) and its associated comment to be the top suggestion so far.

Then, we come to our first 5. Five is obviously larger than 3, but here we encounter our first bug. Our new description length is much shorter than the description from 3, so we don’t get to update our rating or our description.

Finally, we come to our second 5. This runs into the same problem as the first 5, the description is too short.

Our second bug isn’t shown with this example. But imagine that the rating of 3 didn’t exist. Our new highlighted rating would be 5 with a description of “It was pretty neat.” Why would it be that description and not the second one? Because we’re only checking that the rating is greater than the previous high, not greater-than-or-equal to. So we never get a chance to compare description lengths.

With our lodash sample, we don’t have that, because we’re telling it to order based on 2 parameters simultaneously. Additionally, our lodash code is much easier to read, so even if there was a bug, it should be easier to find.

Shifting Focus

One key concept to keep in mind when looking to use lodash is the difference between telling your computer how to do something and telling your computer what you want. The code where we’re iterating over a list and storing temp values is telling our computer how to do something. We’re essentially saying:

  1. Start with the entire list
  2. Now, check that this value is greater than the previous value

    2a. If it is greater, then store that value off.

    2b. If it is not greater, then go back to the next value in the array

But with lodash what we’re telling the computer is “I want the highest rated evaluation with the longest comment” and the computer delivers that to you. By-and-large, you don’t care how it does that, provided it gives you the right answer consistently.

That little ‘trick’ will help you focus on cleaning up your code, as you’ll begin to think about what you want, and less about how you need to get it.

About that Max Function

Oh, and about that example up above of doing arr.max, I didn’t forget about that. Lodash provides the ability to create mixins, which essentially allow you to define your own lodash functions. If we wanted to create our own max function, it might look like this:

_.mixin({
    max: function (arr) {
        return _(arr).sortBy(function(n){ return n;}).last();
    }
}, { chain: false });

We’re telling lodash to order an array by each element in the array, and then to take the last one. The {chain: false} at the end tells lodash that this is a terminal function. Which means we could use it like this:

_(arr).max();

If we didn’t have chain: false we would have needed to call .value() at the end.

tl;dr

Using lodash exposes us to a wide variety of functions that will assist in our computations. They’re named in such a way that it becomes obvious what the function is returning. If we focus on using the vast lodash library, we’ll be able to be more expressive in our code. Being more expressive will make our code easier to understand.