Lodash Faves

January 2016

Bob Holben

@rbholben

bholben.com

slides.com/bholben/lodash-faves

What topics would you like to see?

"Code organization  and how to utilize libraries to speed up development"

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)

  • ~ 230 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

_.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:

  1. Browser compatibility
  2. Flexibility
  3. 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

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 (!_.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

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()

 

_.difference()

Set Theory

a

b

c

a

d

d

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']
_.difference(arr1, arr2);
// ['a', 'a'];

Set Theory

_.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());

No more _.extends() in Lodash V4, but

_.extend() alias has survived V4 release

_.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', '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

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

Bob Holben

@rbholben

bholben.com

slides.com/bholben/lodash-faves

Made with Slides.com