Lodash Faves
March 2016
What is Lodash?
If you've ever grumbled...
-
"Why is JS so cumbersome to..."
-
"Why isn't this built into JS?"
-
"I'm seeing some patterns here"
underscore.js
-
Jeremy Ashkenas (also known for Backbone & Coffeescript)
-
~ 130 methods
-
~ 2M downloads per week
lodash.com
-
John-David Dalton (also known for JSPerf)
-
~ 300 methods
-
~ 4M downloads per week
-
#1 most depended on NPM package
-
Originally a drop-in replacement for Underscore
-
Higher performance

Underscore
+
Lodash
Underdash
V4
V2
?
?
When to consider Lodash
-
More than ~1,000 LOC
-
More than a weekend project
-
Moderate amount of data transformations
-
Using a framework
-
Performance
Core build ~4kB
Full build ~21kB
_.forEach()
_.map()
_.filter()
_.reduce()
var names = [];
users.forEach(function (user) {
names.push(user.name);
});
// names = ['barney', 'fred']
var names = [];
_.forEach(users, function (user) {
names.push(user.name);
});
// names = ['barney', 'fred']
var users = [
{name: 'barney', age: 36},
{name: 'fred', age: 40}
];
// We want names = ['barney', 'fred']
Why bother with Lodash?
3 reasons to consider:
- Browser compatibility
- Flexibility
- Performance
1. Browser Compatibility

- Array
- Object
- String
- Shorthand
- Early loop exit
2. Flexibility
- Array
Array.prototype.forEach()
_.forEach()
3. Performance
var array = _.shuffle(_.range(1000));
Lodash 88% faster on my box
(MBP, Chrome/V8)
Native forEach handles more special cases (sparse arrays) and has more function lookups
array.forEach(function (val) {
return val;
});
_.forEach(array, function (val) {
return val;
});
Lodash vs. Native JS Loops
https://jsperf.com/array-prototype-foreach-vs-each
https://jsperf.com/array-prototype-map-vs-map
https://jsperf.com/array-prototype-filter-vs-filter
https://jsperf.com/array-prototype-reduce-vs-reduce
Lodash > 80% faster than native Array.prototype methods: forEach(), map(), filter(), and reduce()
Using map...
var names = [];
_.forEach(users, function (user) {
names.push(user.name);
});
// names = ['barney', 'fred']
var users = [
{name: 'barney', age: 36},
{name: 'fred', age: 40}
];
// We want names = ['barney', 'fred']
var names = _.map(users, function (user) {
return user.name;
});
var names = _.map(users, function (user) {
return user.name;
});
Map shorthand
var names = _.map(users, 'name');
No more _.pluck() in Lodash V4
_.has()
_.get()
_.set()
var data = {
response: {
code: '500',
method: 'GET',
url: {
href: 'http://...'
},
appl: {
code: '0',
msg: 'Invalid type'
}
}
};
if (data.response &&
data.response.appl &&
data.response.appl.msg) {
message = data.response.appl.msg;
}
if (_.has(data, 'response.appl.msg')) {
message = data.response.appl.msg;
}
message = _.get(data, 'response.appl.msg');
message = _.get(data, 'response.appl.msg', '');
Using has & get...
Cautious navigation...
Or let Lodash do the work...
var data = {
response: {
code: '500',
method: 'GET',
url: {
href: 'http://...'
},
appl: {
code: '0',
msg: 'Invalid type'
}
}
};
_.set(data, 'parsed.appl.msg', message);
Using set...
data.parsed = {};
data.parsed.appl = {};
data.parsed.appl.msg = message;
data.parsed = {
appl: {
msg: message
}
};
Cautious navigation...
Or let Lodash do the work...
or
_.isEmpty()
_.includes()
_.last()
if (myArray.length > 0) {
doSomething();
}
if (myArray) {
doSomething();
}
What I really want to do...
Using isEmpty...

if (myArray.length > 0) {
doSomething();
}
if (!_.isEmpty(myArray)) {
doSomething();
}
if (myArray) {
doSomething();
}
What I really want to do...
Next best thing...
Using isEmpty...
if (myArray.indexOf('foo') !== -1) {
doSomething();
}
if (_.includes(myArray, 'foo')) {
doSomething();
}
Using includes...
if ('foo' in myArray) {
doSomething();
}
What I really want to do...
Next best thing...
No more _.contains() in Lodash V4
- Array
- String
- Object
Testing for a value
- Array.prototype.includes() - ES2016
- String.prototype.includes() - ES2015
JavaScript Built-ins
_.includes()
*Testing for property (not value):
- Object.prototype.hasOwnProperty - tests for property
- in operator - tests for property in prototype chain
Testing for a value in an object is not very useful. Therefore, the utility of _.includes looks to be marginal once built-ins are fully implemented.
var firstUser = users[0];
var lastUser = users[users.length - 1];
var firstUser = _.first(users);
var lastUser = _.last(users);
Using last...
var firstUser = users[0];
var lastUser = users[-1];
What I really want to do...
Next best thing...
var users = [
{name: 'barney', age: 36},
{name: 'fred', age: 40}
];
_.round()
_.max()
_.min()
_.round(3.14159, 3); // 3.142
function round(num, precision) {
return Math.round(Number(num + 'e' + precision)) /
Number('1e' + precision);
}
round(3.14159, 3); // 3.142
Rounding a number...
Write your own function
Or, use Lodash
_.max([12, 25, 3]); // 25
Finding the max/min value...
Math.max.apply(null, myArr); // 25
var myArr = [12, 25, 3];
myArr.reduce(function (prev, curr) {
return Math.max(prev, curr);
}); // 25
Write your own function
Or, use Lodash
or
(large arrays can fail inconsistently)
var users = [
{name: 'barney', age: 36},
{name: 'fred', age: 40}
];
_.maxBy(users, 'age');
// {name: 'fred', age: 40}
Finding the max/min value...
Use _.maxBy() instead of _.max() in Lodash V4
_.uniq()
_.union()
_.intersection()
_.xor
_.difference()
Set Theory
a
b
c
a
d
e
var arr1 = ['a', 'a', 'b', 'c'];
var arr2 = ['b', 'c', 'd', 'e'];
_.uniq(arr1);
// ['a', 'b', 'c']
_.union(arr1, arr2);
// ['a', 'b', 'c', 'd', 'e']
_.intersection(arr1, arr2);
// ['b', 'c']
_.xor(arr1, arr2);
// ['a', 'd', 'e'];
Set Theory
_.difference(arr1, arr2);
// ['a', 'a'];
arr1
arr2
_.create()
_.assign()
var animal = {
initAnimal: function () {},
eat: function () {},
poop: function () {}
};
var dog = Object.create(animal);
dog.initDog = function () {};
dog.bark = function () {};
dog.howl = function () {};
Pure Prototypal Inheritance
var animal = {
initAnimal: function () {},
eat: function () {},
poop: function () {}
};
var dog = _.create(animal, {
initDog: function () {},
bark: function () {},
howl: function () {}
});
_.create()
var pig = function () {
return {
oink: function () {},
snort: function () {}
};
};
Object Composition
var dog = function () {
return {
bark: function () {},
howl: function () {}
};
};
var curly = Object.assign({}, pig(), dog());
Object.assign() is new with ES2015 (not fully supported)
var pig = function () {
return {
oink: function () {},
snort: function () {}
};
};
_.assign()
var dog = function () {
return {
bark: function () {},
howl: function () {}
};
};
var curly = _.assign({}, pig(), dog());
_.range()
_.times()
_.countBy()
_.sortBy()
_.range(10);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Using range...
for (var i = 0; i < 6; i++) {
$('#nextBtn').click();
}
_.times(6, function () {
$('#nextBtn').click();
});
Using times...
_.countBy(users, 'category');
// {father: 2, mother: 2}
var users = [
{name: 'fred', category: 'father'},
{name: 'wilma', category: 'mother'},
{name: 'barney', category: 'father'},
{name: 'betty', category: 'mother'}
];
Using countBy...
var users = [
{name: 'barney', age: 36},
{name: 'fred', age: 40},
{name: 'betty', age: 35},
{name: 'wilma', age: 39}
];
_.map(_.sortBy(users, 'age'), 'name');
// ['betty', 'barney', 'wilma', 'fred']
Using sortBy...
_.sortBy(users, 'age');
// [{name: 'betty', age: 35},
// {name: 'barney', age: 36},
// {name: 'wilma', age: 39},
// {name: 'fred', age: 40}]
Method Wrapping
vs
Method Chaining
Method Wrapping
var users = [
{name: 'fred', age: 40, gender: 'male'},
{name: 'wilma', age: 39, gender: 'female'},
{name: 'pebbles', age: 2, gender: 'female'},
{name: 'barney', age: 36, gender: 'male'},
{name: 'betty', age: 35, gender: 'female'},
{name: 'bamm bamm', age: 2, gender: 'male'}
];
// We want the youngest adult male
_.first(_.sortBy(_.filter(_.filter(
users,['gender', 'male']),
filterAdult), 'age')).name; // 'barney'
Yuck!
Method Chaining
var result = _(users).filter(['gender', 'male'])
.filter(filterAdult)
.sortBy('age')
.first()
.name; // 'barney'
var users = [
{name: 'fred', age: 40, gender: 'male'},
{name: 'wilma', age: 39, gender: 'female'},
{name: 'pebbles', age: 2, gender: 'female'},
{name: 'barney', age: 36, gender: 'male'},
{name: 'betty', age: 35, gender: 'female'},
{name: 'bamm bamm', age: 2, gender: 'male'}
];
// We want the youngest adult male
Method Chaining
-
Improved readability
-
"How to Speed Up Lo-Dash x100? Introducing Lazy Evaluation" - Filip Zawada
Method chaining is great, but...
Method Composition
-
Pruning the 21kb kitchen sink lodash lib is complex
-
"Why using _.chain is a mistake" - Izaac Shroeder
-
Functional programming (currying & composition)
What to do?
Tune it for performance
Use method composition
Big user base?
Small app size?
Lodash & Angular 1
Lodash as a Global
npm install
bower install
Make globally accessible with script tag
The Angular Way:
Dependency Injection
angular.module('lodash', [])
.factory('_', function() {
return window._;
});
This does not stop you from using global _
angular.module('lodash', [])
.factory('_', function() {
var lodash = window._;
// Don't delete if needed by 3rd party libs!!
delete window._;
return lodash;
});
The Angular Way:
Dependency Injection
angular.module('lodash', [])
.factory('_', function() {
var lodash = _.noConflict();
return lodash;
});
The Angular Way
with a Lodash Twist
Highlights
-
Get to know Lodash's jewels
-
Favor Lodash looping constructs
-
Get comfortable with _.map() & _.filter()
-
Grow to love _.has() & _.get()
-
Method chaining is sweet
Tips
-
Review Lodash docs regularly
-
Explore in the browser console on Lodash page
Lodash Faves - March 2016
By Bob Holben
Lodash Faves - March 2016
- 1,664