All notes
Lodash

Intro

Lodash makes JavaScript easier by taking the hassle out of working with arrays, numbers, objects, strings, etc.

Similar to "underscore".

lodash.com.

Import



// Load the full build.
var _ = require('lodash');

// Load method categories.
var array = require('lodash/array');
var object = require('lodash/fp/object');
 
// Cherry-pick methods for smaller browserify/rollup/webpack bundles.
var at = require('lodash/at');
var curryN = require('lodash/fp/curryN');

Lang



_.isUndefined(void 0);
// => true
_.isUndefined(null);
// => false

Array



////// Split into chunks.
_.chunk(['a', 'b', 'c', 'd'], 2);
// => [['a', 'b'], ['c', 'd']]
_.chunk(['a', 'b', 'c', 'd'], 3);
// => [['a', 'b', 'c'], ['d']]

////// Remove all nil's.
_.compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]

////// Concat.
_.concat([1], 2, [3], [[4]]);
// => [1, 2, 3, [4]]

////// difference
_.difference([2, 1], [2, 3]);
// => [1]
_.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
// => [1.2]
// The `_.property` iteratee shorthand.
_.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
// => [{ 'x': 2 }]

////// drop
_.drop([1, 2, 3]);
// => [2, 3]
_.drop([1, 2, 3], 2);
// => [3]
_.drop([1, 2, 3], 5);
// => []
_.drop([1, 2, 3], 0);
// => [1, 2, 3]
_.dropRight([1, 2, 3]);
// => [1, 2]

////// fill
_.fill(Array(3), 2);
// => [2, 2, 2]
_.fill([4, 6, 8, 10], '*', 1, 3);
// => [4, '*', '*', 10]

///// without
_.without([2, 1, 2, 3], 1, 2);
// => [3]

Collection

each/forEach, eachRight/forEachRight

each/forEach. _.forEach(collection, [iteratee=_.identity]). Iterates over elements of collection and invokes iteratee for each element. The iteratee is invoked with three arguments: (value, index|key, collection). Iteratee functions may exit iteration early by explicitly returning false.

As with other "Collections" methods, objects with a "length" property are iterated like arrays. To avoid this behavior use _.forIn or _.forOwn for object iteration.

eachRight: like _.forEach except that it iterates over elements of collection from right to left.



_.forEach([1, 2], function(value) {
  console.log(value);
});
 
_.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
  console.log(key);
});

includes



// _.includes(collection, value, [fromIndex=0])
// Checks if value is in collection.

_.includes([1, 2, 3], 1);
// => true
 
_.includes([1, 2, 3], 1, 2);
// => false
 
_.includes({ 'a': 1, 'b': 2 }, 1);
// => true
 
_.includes('abcd', 'bc');
// => true

orderBy, sortBy



//---------- orderBy

// Like _.sortBy except that it allows specifying the sort orders of the iteratees to sort by.
// By default, in ascending order.

var users = [
  { 'user': 'fred',   'age': 48 },
  { 'user': 'barney', 'age': 34 },
  { 'user': 'fred',   'age': 40 },
  { 'user': 'barney', 'age': 36 }
];

// Sort by `user` in ascending order and by `age` in descending order.
_.orderBy(users, ['user', 'age'], ['asc', 'desc']);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]

//---------- sortBy

// sortBy performs a stable sort.
// Only sort in ascending order.

var users = [
  { 'user': 'fred',   'age': 48 },
  { 'user': 'barney', 'age': 36 },
  { 'user': 'fred',   'age': 40 },
  { 'user': 'barney', 'age': 34 }
];

_.sortBy(users, [function(o) { return o.user; }]);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]

_.sortBy(users, ['user', 'age']);
// => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]

Function

defer, delay

Defers invoking the func until the current call stack has cleared. Any additional arguments are provided to func when it's invoked.



_.defer(function(text) {
  console.log(text);
}, 'deferred');
// => Logs 'deferred' after one millisecond.

_.delay(function(text) {
  console.log(text);
}, 1000, 'later');
// => Logs 'later' after one second.

memoize

Creates a function that memoizes the result of func.



var object = { 'a': 1, 'b': 2 };
var other = { 'c': 3, 'd': 4 };
 
var values = _.memoize(_.values);
values(object);
// => [1, 2]
 
values(other);
// => [3, 4]
 
object.a = 2;
values(object);
// => [1, 2]
 
// Modify the result cache.
values.cache.set(object, ['a', 'b']);
values(object);
// => ['a', 'b']
 
// Replace `_.memoize.Cache`.
_.memoize.Cache = WeakMap;

once

Creates a function that is restricted to invoking func once. Repeat calls to the function return the value of the first invocation.

rest

Creates a function that invokes func with the this binding of the created function and arguments from start and beyond provided as an array. This method is based on the "rest" parameter.



var say = _.rest(function(what, names) {
  console.log(names);
  return what + ' ' + _.initial(names).join(', ') +
    (_.size(names) > 1 ? ', & ' : '') + _.last(names);
});
 
say('hello', 'fred', 'barney', 'pebbles');
// ["fred", "barney", "pebbles"]
// => 'hello fred, barney, & pebbles'

wrap

Creates a function that provides value to wrapper as its first argument.



var p = _.wrap(_.escape, function(func, text) {
  return '<p>' + func(text) + '</p>';
});
 
p('fred, barney, & pebbles');
// => '<p>fred, barney, &amp; pebbles</p>'

Object

assign, extend



// Source objects are applied from left to right. Subsequent sources overwrite property assignments of previous sources.
_.assign(object, [sources]);

function Foo() {
  this.a = 1;
}
 
function Bar() {
  this.c = 3;
}
 
Foo.prototype.b = 2;
Bar.prototype.d = 4;
 
_.assign({ 'a': 0 }, new Foo, new Bar);
// => { 'a': 1, 'c': 3 }

// _.assignIn is like _.assign except that it iterates over own and inherited source properties.
_.assignIn({ 'a': 0 }, new Foo, new Bar);
// => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }

// _.extend is just an alias of _.assignIn.
_.extend({ 'a': 0 }, new Foo, new Bar);
// => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }

mapValues



var users = {
  'fred':    { 'user': 'fred',    'age': 40 },
  'pebbles': { 'user': 'pebbles', 'age': 1 }
};

_.mapValues(users, function(o) { return o.age; });
// => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)

// The `_.property` iteratee shorthand.
_.mapValues(users, 'age');
// => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)

pick, pickBy, omitBy



var object = { 'a': 1, 'b': '2', 'c': 3 };
 
_.pick(object, ['a', 'c']);
// => { 'a': 1, 'c': 3 }

_.pickBy(object, _.isNumber);
// => { 'a': 1, 'c': 3 }

_.omit(object, ['a', 'c']);
// => { 'b': '2' }

_.omitBy(object, _.isNumber);
// => { 'b': '2' }

values, valuesIn



function Foo() {
  this.a = 1;
  this.b = 2;
}

Foo.prototype.c = 3;

_.values(new Foo);
// => [1, 2] (iteration order is not guaranteed)
_.valuesIn(new Foo);
// => [1, 2, 3] (iteration order is not guaranteed)

_.values('hi');
// => ['h', 'i']

String

underscore.string

epeli.github.io.

For underscore, you need to npm install underscore.string.

camelCase, kebabCase, snakeCase, startCase



_.camelCase('Foo Bar');
// => 'fooBar'
_.camelCase('--foo-bar--');
// => 'fooBar'
_.camelCase('__FOO_BAR__');
// => 'fooBar'

_.kebabCase('Foo Bar');
// => 'foo-bar'
_.kebabCase('fooBar');
// => 'foo-bar'
_.kebabCase('__FOO_BAR__');
// => 'foo-bar'

_.snakeCase('Foo Bar');
// => 'foo_bar'
_.snakeCase('fooBar');
// => 'foo_bar'
_.snakeCase('--FOO-BAR--');
// => 'foo_bar'

_.startCase('--foo-bar--');
// => 'Foo Bar'
_.startCase('fooBar');
// => 'Foo Bar'
_.startCase('__FOO_BAR__');
// => 'FOO BAR'

capitalize, lowerCase, lowerFirst, toLower



_.capitalize('FRED');
// => 'Fred'

_.lowerCase('--Foo-Bar--');
// => 'foo bar'
_.lowerCase('fooBar');
// => 'foo bar'
_.lowerCase('__FOO_BAR__');
// => 'foo bar'

_.lowerFirst('Fred');
// => 'fred'
_.lowerFirst('FRED');
// => 'fRED'

_.toLower('--Foo-Bar--');
// => '--foo-bar--'
_.toLower('fooBar');
// => 'foobar'
_.toLower('__FOO_BAR__');
// => '__foo_bar__'

deburr, pad



_.deburr('déjà vu');
// => 'deja vu'

_.pad('abc', 8);
// => '  abc   '
_.pad('abc', 8, '_-');
// => '_-abc_-_'
_.pad('abc', 3);
// => 'abc'

Where is: split

Use JS's own string split function instead.

Event Functions

bind

Important: use native bind instead! See below.



function greet(greeting, punctuation) {
  return greeting + ' ' + this.user + punctuation;
}
 
var object = { 'user': 'fred' };
 
var bound = _.bind(greet, object, 'hi');
bound('!');
// => 'hi fred!'
 
// Bound with placeholders.
var bound = _.bind(greet, object, _, '!');
bound('hi');
// => 'hi fred!'

Use native bind instead!

github.com: appium issues.



_.bind(function(foo) {
  // my awesome function
}, this);

//----- Instead, we prefer native bind:

function(foo) {
  // my awesome function
}.bind(this);

debounce, throttle, requestAnimationFrame

A very good article by a FE expert: css-tricks.com.

debounce: Grouping a sudden burst of events (like keystrokes) into a single one.
Imagine you are in an elevator. The doors begin to close, and suddenly another person tries to get on. The elevator doesn't begin its function (change floors), the doors keep open. Now it happens again with another person. The elevator is delaying its function (moving floors), but optimizing its resources. When the people come in frequently enough, the elevator will always wait; when there is a break, e.g. no people come in for a while, then the function will be fired.

throttle: Guaranteeing a constant flow of executions every X milliseconds. Like checking every 200ms your scroll position to trigger a CSS animation.

requestAnimationFrame: a throttle alternative. When your function recalculates and renders elements on screen and you want to guarantee smooth changes or animations. Note: no IE9 support.

To trigger the function execution immediately, so it behaves exactly as the original non-debounced handler, use "{leading:true, trailing:false}". By default, only the trailing edge is enabled.



// WRONG
$(window).on('scroll', function() {
  _.debounce(doSomething, 300); // 300: The number of milliseconds to delay.
});

// RIGHT
$(window).on('scroll', _.debounce(doSomething, 200));