Skip to main content
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Loveena Dsouza and Ritika Chowdhary
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Loveena Dsouza Ritika Chowdhary

The Philosophy Of Extending Lodash In JavaScript

By
Published in Comments (8)

At work, I sometimes get into a philosophical debate about our use of the Lodash library. I love lodash and use it in our AngularJS applications all the time. But, I will often extend the lodash library with additional utility functions. This is where things gets heated. Some of the engineers question my desire to extend lodash when I could implement the same functionality using existing lodash features. To me, this mentality misses the point and often leads to a duplication of effort in the code.

Run this demo in my JavaScript Demos project on GitHub.

The more code it takes to implement a feature, the harder it is to understand and maintain. As such, if we can remove or encapsulate complexity, it's inherently a win for our application (and our engineering team). This is why I'll try to factor out common lodash use-cases into their own extensions. This provides a smaller, more intuitive interface that is less brittle and easier to maintain.

To make this concrete, consider the task of setting a value in each item of a collection. Certainly, you can do this using existing lodash functionality, which can range from simple and verbose (forEeach) to compact and cryptic (partial application). But, by creating a lodash extension, we can create a beautiful marriage between simple and compact.

In the following code, I'm going to use three different approaches to setting a value on each item in a collection. The first two approaches attempt to use existing lodash features, with subjective levels of success. The third approach still uses existing lodash functionality; but, it encapsulates the complexity and verbosity behind a lodash extension:

<!doctype html>
<html>
<head>
	<title>The Philosophy Of Extending Lodash In JavaScript</title>
</head>
<body>

	<h1>
		The Philosophy Of Extending Lodash In JavaScript
	</h1>

	<p>
		<em>All the magic is in the console</em>.
	</p>

	<script type="text/javascript" src="../../vendor/lodash/lodash-3.9.3.min.js"></script>
	<script type="text/javascript">

		// Start out with our initial collection. We'll be augmenting it using the
		// lodash library.
		var friends = [
			{
				id: 1,
				name: "Kim"
			},
			{
				id: 2,
				name: "Sarah"
			},
			{
				id: 3,
				name: "Tricia"
			}
		];


		// ---
		// APPROACH 1: Use explicit for-each method so you can see exactly what the code
		// is doing. Easy to understand, easy to maintain, but verbose.
		// ---


		// Use an inline operator.
		_.forEach(
			friends,
			function operator( friend, i ) {

				friend.createdAt = _.now();

			}
		);


		// ---
		// APPROACH 2: Using unfortunate complexity. Hard to understand. Personally, when
		// I see code like this, I think the developer was trying to be too clever. It's
		// not about "you" bro, it's about the "next guy" trying to maintain your code.
		// ---


		// Using partial functions.
		_.forEach( friends, _.partial( _.set, _, "isActive", true ) );


		// ---
		// APPROACH 3: Factor out common use-cases into an actual extension for lodash
		// itself. This way, the complexity and verbosity of common tasks can be
		// encapsulated behind well-named, easy-to-understand intentional methods.
		// ---


		// Extend lodash with some custom functions.
		_.mixin({
			setEach: function( collection, path, value ) {

				var result = _.forEach(
					collection,
					function operator( item, index ) {

						_.set( item, path, value );

					}
				);

				return( result );

			},

			extendEach: function( collection, source ) {

				var result = _.forEach(
					collection,
					function operator( item, index ) {

						_.extend( item, source );

					}
				);

				return( result );

			}
		});

		// Now that we've extended lodash, we have a very simple function that is
		// intuitive and reusable. And, if it's ever not enough, you can fall back to
		// using a more explicit, inline approach.
		_.setEach( friends, "isBFF", true );

		// Use our other custom function.
		_.extendEach(
			friends,
			{
				foo: "bar",
				hello: "world"
			}
		);


		// CAUTION: The fact that I am using _.ary() here to apply the console.log()
		// function is a bit tongue-in-cheek. This is more complex that I would like it
		// to be. But, you can't help but enjoy the challenge of making overly-complex
		// algorithms.
		_.forEach( friends, _.ary( console.log, 1 ) );

	</script>
</body>
</html>

The first approach, using the forEach() method, is very easy to understand and is very flexible. But, it's somewhat verbose.

The second approach, using forEach() in conjunction with partial application, is compact, but extremely cryptic. Like a Regular Expression, it might make sense to you when you write this code; but, for the engineer that has to come after you and maintain it, such function composition can often be hard to decipher. I would suggest, when possible, avoiding code like this that attempts to be too clever.

The third approach, extending lodash, creates the most compact code and provides a method name and signature that is easy to understand and consume. In my opinion, in this particular use case, the third approach makes for the most elegant solution.

I am not saying that you should factor out every possible lodash use case. In many cases, having an inline forEach() call is absolutely the right way to go. But, when you see common patterns emerge, moving them into a lodash extension will help you keep your code DRY (Do not Repeat Yourself).

Want to use code from this post? Check out the license.

Reader Comments

4 Comments

Yay for mixins! In node-land you can combo `_.mixin` with `_.runInContext` to ensure extension are added to a pristine lodash instance and not augmenting the `lodash` package itself:

```js
var _ = require('lodash').runInContext();
_.mixin(...);
```

15,848 Comments

@Jdalton,

Very interesting. That reminds of jQuery's old .sub() method which, from what I recall, allowed you to sub-class jQuery for use in a particular context. Do you have any thoughts on the right approach to this in Node? Shooting from the hip, I would probably want to create a local lodash module that turns around includes the node-based module. So, something like this:

./lodash.js:
=========
module.exports = require( "lodash" ).runInContext();

module.exports.mixing( .... )
=========

... then, in the rest of my app, my other core modules would include my version of lodash (ie, require( "./lib/util/lodash" )) and not the underlying library?

Or am I over-thinking it?

11 Comments

Good article ben, I haven't ever looked into lodash's mixin functionality - good stuff.

Also, just wanted to throw out that you could also consider using map() for your purposes:

friends = _.map(friends, function(item) {
item.createdAt = _.now();
return item;
});

15,848 Comments

@Ryan,

As an aside, how great is the "_.now()" method?! I can't tell you how many times I've had to create that method before I discovered it in lodash :D

11 Comments

Yeah, its great - I sing the praises of lodash on a regular basis, and always finding something new. Have you ever used _.template() ? Might not be as useful for you with angular, but its great.

15,848 Comments

@Ryan,

I think I looked at it once a few years ago; or rather, I looked at the Underscore implementation - I am not sure if it's any different. Right now, on the client, I use AngularJS pretty heavily; but, this could be really useful on the server (in Node.js).

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel