Lodash is not (only) for list manipulation!

This is a continuation of my previous post about Higher-order functions in Lodash. Most people know Lodash from constructs like this:

But Lodash is much more than a list manipulation library. In this post I would like to shed a light on some less popular, yet in my opinion, extremely useful Lodash features.

Disclaimer: This article assumes that the reader is familiar with concept of higher-order functions and knows how functions like _.curry() and _.partial() work. Moreover, in this article when I refer to “Lodash” I mean thelodash/fp variant of Lodash. If you haven’t heard of lodash/fp, higher-order functions or just need some refresh of your memory, please have a look at my previous article on Higher-order functions in Lodash.

One of the things I love in Lodash is that it is extremely flexible and adaptable. Even if it doesn’t have the specific function you need, there is a high chance that you can build one with just a few lines of code. Lodash’s authors placed extension points throughout the codebase that allow developers to further customize its behavior. One such extension point are Customizers.

Customizers

Object-Oriented programmers will recognize Customizers as the Strategy pattern from the famous Design Patterns: Elements of Reusable Object-Oriented Software book by The “Gang of Four”.

Customizers allow you to significantly change object behavior by substituting one strategy for another.

Let’s have a look at how customizers work in practice. Suppose we have a partial contact information, that we would like to combine into one object. As you might expect Lodash already provides a function that does the job for us. _.merge() function merges two objects, property by property:

However, if same property is present in both merged objects property value from last object wins. In our example it is unfortunate, as we loose information about one of the contact’s phone number.

Fortunately there is an alternative version of _.merge() that accepts an additional function which allows to customize a way in which properties are merged. This customization function is going to be invoked for every property (also nested properties) that should be merged (properties from the second merged object). Values of property to be merged will be passed as two first parameters. Let’s give it a try:

Bonus: Alternatively customizer can be defined this way: let customizer = _.cond([[_.isArray, _.concat]]);

If one of merged properties points to an array then our customizer returns a new array that will include values from both merged objects. Notice that if merged value is not an array our customizer won’t return any value (or to put it in another words — it will return undefined). In such situation Lodash will fallback to default strategy (the one from _.merge() function).

But why should we limit ourselves just to arrays concatenation?
Here is how we can make our customizer even more generic:

In this new version of customizer, if second of the merged objects contains any functions then instead of assigning those functions to resulting object, we simply invoke them passing as parameters values from matching property from the first merged object.

Now we will fix this customizer to as a first parameter of _.mergeWith(). Let’s call this resulting function patch:

var patch = recipe => _.mergeWith(customizer, _, recipe);

Remember that all lodash/fp functions are auto-curried, so we can pass them subset of parameters, as well as parameter placeholders _ and in result we will get new function with some of the parameters fixed.

The resulting patch() is a higher-order function that returns new function that transforms objects based on provided recipe. Recipes are formulated in a pretty declarative way, by explicitly telling which function to use in order to merge given property. If property points to anything other than function, default strategy applies.

Advanced note: order of parameters in _.mergeWith(customizer, object, source) is a little bit unfortunate, as it accepts data (object) parameter as a second and not last parameter. If it would be the other way we could fully benefit from curring and define patch simply as:
var patch = _.mergeWith(customizer)
However, actual order of parameters forced us to skip second of its parameter using _.
Alternatively, we could re-arrange parameters using
_.rearg() like this:
var mergeRearg = _.rearg(_.mergeWith, [0, 2, 1]);
var patch = mergeRearg(customizer);
or just (using _.flip()):
var patch = _.flip(_.mergeWith(customizer));
_.flip() and _.rearg() are yet another proof of Lodash flexibility.

OK, once we defined our patch() function, let’s see what it is capable of. We will start with re-implementing contact information example:

Now, let’s imagine we want to be able to toggle a favorite flag of our contact:

In Lodash _.mergeWith() is recursive, thanks to that our patch() function supports nested properties out-of-the-box:

Have you noticed how we avoided boilerplate null checks?

As last exercise, lets write a patching function that tries to parse address line and extract zip code, street and city components. To chain multiple operations (execute regexp, extract matched regexp groups into an array, build object from an array) we will use _.flow() function:

Now, we can combine all those transformations together:

We can also do it this way:

Operation first, data last

Notice how in all examples we first define transformations, then compose them, and in the end we pass actual data to the transformation. The transformation itself doesn’t depend on data, only on structure of the data it accepts. It is much different than in most classical, object-oriented style in which operations are bound to some context (for example this or some parent scope variable). In the following example findActiveItems() function depends on items array:

This context-dependency makes functions less reusable, as they cannot be separated from their context. Unlike object-oriented style, in functional programming we try to separate operations from data as much as possible. One way to accomplish this, is to defer passing data as late as possible. It is idiomatic (characteristic) for functional libraries (like lodash/fp or Ramda) to accept data as last parameter.

To-Do list implementation

Let’s get back to our patch() function and see how far we can push this simple implementation. How complicated logic can we express using this naive patch() function, before we fall back to more classical (imperative) style of programming? As a benchmark let’s try to implement all features of famous TodoMVC project. Of course we will focus on domain/model part and we will skip any UI/view related parts.

Let’s enumerate all features of To-Do List supported by TodoMVC:

  1. create new To-Do item,
  2. mark all items as completed,
  3. clear all completed items,
  4. toggle single item as completed,
  5. rename single item,
  6. remove single item.

We will go one-by-one with this list, but first lets define how model of our To-Do list will look like:

1. Creating new To-Do item

Adding new To-Do item is pretty straightforward after what we learned from previous examples.

2. Mark all items as completed

First we will create function for completing single item and then we will apply it to all items in list:

We have also created an alias forAll for _.map function as it will improve readability.

3. Clear all completed items

Removing completed items is very similar to previous listing. We have used _.matches() function to filter completed items.

Similarly we created removeIf as an alias for _.reject.

4. Toggle single item as completed

Defining function that will toggle completed flag for single item is easy:

Toggling single item in a To-Do list and leave all the others intact, is a whole different story. In order to do it, we will first create new higher-order function that will invoke the function passed as a parameter conditionally, based on given predicate:

onlyIf() function is a little bit too imperative. Maybe Lodash can help us with this somehow? Of course it can! Take a look at _.cond(). Now we have:

Now we need one more function:

I must say that otherwise() looks a little bit too extreme. Probably imperative version would look much more understandable. I leave reader with the task (of rewriting this function) as an excercise. Fragment otherwise(onlyIf(pow), _.isNumber), _.constant('number expected!') is by no means any better. It doesn’t read naturally. Definitely it is less readable than powN(n){ return _.isNumber(n) ? n * n : 'number expected!'}. Maybe we went one bridge too far. But let’s try one more trick, before we give up. Let’s assign both function to Function.prototype and pass original function as this parameter:

We had to switch from arrow notation (() => {}) to function expression, as arrow functions do not bind own this parameter. Now we can get back to our original task: toggling particular item.

4a. Customized Matcher

As you can see in code listing above, in order to complete single Todo-Item we have to pass its full title. It is a little bit inconvenient. What if we would like to complete every item that contains “Learn” or “learn” in it’s title? We need custom matching function that also accepts regular expressions. To achieve that we will use already familiar feature of Lodash customizers, but this time we will apply it to _.isMatchWith() function instead of _.mergeWith().

We can re-use our previous customizer and extend it with support for regexp’s:

Our enhanced customizer effectively transformed _.isMatchWith() into something even more flexible than _.conforms() as it allows to match object properties against: fixed values, regular expressions and function predicates:

5. Rename single item + 6. remove single item

With helper functions introduced in previous paragraphs, remaining two functionalities are dead simple:

All the pieces together

Let’s put all the pieces together. I have split function definitions in two groups. First group consist of more abstract and more reusable (higher-order) functions. Second group includes more domain-specific functions.

You can also find full listing and play around with it at this JS Fiddle.

Summary

In the article we have been investigating customization capabilities of Lodash library. As a result we have build a simple, yet powerful, Domain-Specific Language for declarative transformation of JSON objects in form of patches. Thanks to flexibility and extensibility of Lodash, we made it, by writing only few lines of code.

In the end we were able to compose extremely simple functions into more sophisticated patches, that reads almost as a natural language.

Is this functional approach better/cleaner/fancier/name your own adjective here than classical imperative one? I leave reader with this question to decide on his own. Regardless of the answer however, it is definitely worth to be aware of all this cool customization features provided by Lodash.

In next article we will extend our example with a user interface and build a fully capable To-Do list application. Stay tuned!