Skip to content

Instantly share code, notes, and snippets.

@hungtran-it
Created November 27, 2015 12:51
Show Gist options
  • Select an option

  • Save hungtran-it/60dbee0e49901798a70d to your computer and use it in GitHub Desktop.

Select an option

Save hungtran-it/60dbee0e49901798a70d to your computer and use it in GitHub Desktop.
Lodash
Finding keys and values
Objects, in Vanilla JavaScript, use the same syntax as arrays
for accessing property
values.That is, the square bracket notation but typically with a human - readable
string, instead of a numerical index.However, the same issues that exist with
numerical indices and arrays exist with objects and keys too.Just because the key
is a string doesn 't mean that we know which keys are available. Sometimes, we
have to search the object to find the key we 're looking for.
We use the findKey()
function to locate the key of the first object property that
callback returns truthy
for:
var object = {
name: 'Gene',
age: 43,
occupation: 'System Administrator'
};
_.findKey(object, function(value) {
return value === 'Gene';
});
// → "name"
Here, the result is name;
since it 's the first property value our callback to findKey()
returns true
for.Strangely enough, the pluck style shorthand doesn 't work the way
you think it might.Calling _.findKey(object, 'Gene') doesn 't find anything.
That 's because it'
s treating each of the property values as nested objects.Here 's an
example of how the where style shorthand works with this
function:
var object = {
programmers: {
Keith: 'C',
Marilyn: 'JavaScript'
},
designers: {
Lori: 'CSS',
Marilyn: 'HTML'
}
};
_.findKey(object, {
Marilyn: 'JavaScript'
});
// → "programmers"
Basic For Each
Just as we saw in the previous chapter, objects can be iterated, just as arrays— they 're
both collections.While the mechanism to do so is slightly different, Lo - Dash abstracts
those differences away behind a unified
function API, as shown in the following code:
var object = {
name: 'Vince',
age: 42,
occupation: 'Architect'
},
result = [];
_.forOwn(object, function(value, key) {
result.push(key + ': ' + value);
});
// →
// [
// "name: Vince",
// "age: 42",
// "occupation: Architect"
// ]
The preceding code should look somewhat familiar.It 's just like the forEach()
function.Instead of the index, the second argument passed to the callback
function
is the property key.
The _.forOwn() and _.forEach() functions behave identically
when applied to an object.Both of these functions share the same
base
function that 's used to iterate over collections. Lo-Dash has
several base functions that are generic enough to serve many
purposes.While these aren 't exposed as a part of the public API,
they make the exposed functions smaller and more comprehensible.
var object = {
name: 'Lois Long',
age: 0,
occupation: null
};
_.omit(object, function(value) {
return !(!_.isBoolean(value) && value);
});
// → { name: "Lois Long" }
Rejecting items
Rejecting works much in the same way as filtering does.In the
case of filtering, you
know what you want.In the
case of rejecting, you know what you don 't want. These
rejection operations can be chained together to build complex queries, as shown in
the following code:
var object = {
first: 'Conrad',
last: 'Casey',
age: 37,
enabled: true
};
_(object)
.reject(_.isBoolean)
.reject(_.isString)
.first()
.toFixed(2);
// → "37.00"
var collection = [
1414728000000,
1383192000000,
1351656000000,
1320033600000
];
_(collection)
.map(function(item) {
return new Date(item);
})
.every(function(item) {
return item.getMonth() === 9 && item.getDate() === 31;
});
// → true
Using partials
A handy pattern to solve the generic argument issues that arise is to use partials,
that is, partially apply
function arguments using the partial()
function.This lets
us build functions at runtime that can be used repeatedly, without having to always
apply the same arguments.Sometimes it 's not even feasible to provide function
arguments.The following is an example of using partials:
var flattenProp = _.compose(_.flatten, _.prop),
skills = _.partialRight(flattenProp, 'skills'),
names = _.partialRight(flattenProp, 'name');
var collection = [{
name: 'Danielle',
skills: ['CSS', 'HTML', 'HTTP']
}, {
name: 'Candice',
skills: ['Lo-Dash', 'jQuery']
}, {
name: 'Larry',
skills: ['KineticJS', 'Jasmine']
}, {
name: 'Norman',
skills: ['Grunt', 'Require']
}];
_.contains(skills(collection), 'Lo-Dash');
// → true
_.contains(names(collection), 'Candice');
// → true
Our flattenProp()
function is a composition of flatten() and prop().
The result that is returned is a flattened array.So,
if any of these property
values were themselves arrays, they just get added to the single array.
var collection = _.map(_.range(100), function(item) {
return {
id: item,
age: _.random(50),
enabled: !!_.random()
};
});
var indexed = _.groupBy(collection, function(item) {
return +item.enabled * item.age;
});
console.time('where');
console.log(_.where(collection, {
age: 25,
enabled: true
}));
console.timeEnd('where');
// →
// [
// { id: 23, age: 25, enabled: true },
// { id: 89, age: 25, enabled: true }
// ]
// where: 5.528ms
console.time('indexed');
console.log(indexed[25] || []);
console.timeEnd('indexed');
// →
// [
// { id: 23, age: 25, enabled: true },
// { id: 89, age: 25, enabled: true }
// ]
// indexed: 0.712ms
var collection = _.range(1000000).reverse();
console.time('motivated');
_.take(_.filter(collection, function(item) {
return !(item % 10);
}), 10);
console.timeEnd('motivated');
console.time('lazy');
_(collection)
.filter(function(item) {
return !(item % 10);
})
.take(100)
.value();
console.timeEnd('lazy');
// →
// motivated: 8.454ms
// lazy: 0.889ms
Partials
To create a partial
function using Lo - Dash, you use the partial()
function.The
resulting
function then has some arguments presupplied— we don 't have to supply
them again when called.This concept is really useful when we need to dynamically
supply arguments to a
function, just before it 's passed to a new context where those
arguments aren 't available. This is also the case with callbacks, as shown in the
following example:
function sayWhat(what) {
return 'Say, ' + what;
}
var hello = _.partial(sayWhat, 'hello'),
goodbye = _.partial(sayWhat, 'goodbye');
hello();
// → "Say, hello"
goodbye();
// → "Say, goodbye"
The sayWhat()
function builds a simple string based on the supplied string
argument.The two calls to partial() that follow supply this argument.The
hello() and goodbye() functions, when called, will call sayWhat() with their
respective partial arguments.
As we 've seen so far in this chapter, many of the Lo-Dash functions that deal with
functions
return new ones.They also support the arguments passed by the caller.
This is valuable because adding new parameters to our functions doesn 't require
changes to our
function bindings, as shown here:
function greet(greeting, name) {
return greeting + ', ' + name;
}
var hello = _.partial(greet, 'hello'),
goodbye = _.partial(greet, 'goodbye');
hello('Fran');
// → "hello, Fran"
goodbye('Jacob');
// → "goodbye, Jacob"
Function decorators
We can utilize the wrap()
function to decorate a value or another
function with
specific behavior.As with all other Lo - Dash
function helpers, one advantage of
using wrap() is that the caller of the generated
function can supply more data
via arguments, as demonstrated in the following code:
function strong(value) {
return '<strong>' + value + '</strong>';
}
function regex(exp, val) {
exp = _.isRegExp(exp) ?
exp : new RegExp(exp);
return _.isUndefined(val) ?
exp : exp.exec(val);
}
var boldName = _.wrap('Marianne', strong),
getNumber = _.wrap('(\\d+)', regex);
boldName();
// → "<strong>Marianne</strong>"
getNumber(
getNumber('abc123')[1];
// → "123"
Function decorators
We can utilize the wrap()
function to decorate a value or another
function with
specific behavior.As with all other Lo - Dash
function helpers, one advantage of
using wrap() is that the caller of the generated
function can supply more data
via arguments, as demonstrated in the following code:
function strong(value) {
return '<strong>' + value + '</strong>';
}
function regex(exp, val) {
exp = _.isRegExp(exp) ?
exp : new RegExp(exp);
return _.isUndefined(val) ?
exp : exp.exec(val);
}
var boldName = _.wrap('Marianne', strong),
getNumber = _.wrap('(\\d+)', regex);
boldName();
// → "<strong>Marianne</strong>"
getNumber(
getNumber('abc123')[1];
// → "123"
Limiting call counts
There are two Lo - Dash functions that deal with counting the number of times a given
function is called.The after()
function will execute a callback after the composed
function is called a given number of times.The once()
function constrains the given
function to being called only once.Let 's look at after() and see how it works:
function work(value) {
progress();
}
function reportProgress() {
console.log(++complete + '%');
progress = complete < 100 ?
_.after(0.01 * collection.length, reportProgress) :
_.noop;
}
var complete = 0,
collection = _.range(9999999),
progress = _.noop;
reportProgress();
_.forEach(collection, work);
// →
// 1%
// 2%
// 3%
// …
function toCelsius(degrees) {
return (degrees - 32) * 5 / 9;
}
function toFahrenheit(degrees) {
return degrees * 9 / 5 + 32;
}
var celsius = _.memoize(toCelsius),
fahrenheit = _.memoize(toFahrenheit);
toCelsius(89).toFixed(2) + ' C';
// → "31.67 C"
celsius(89).toFixed(2) + ' C';
// → "31.67 C"
toFahrenheit(23).toFixed(2) + ' F';
// → "73.40 F"
fahrenheit(23).toFixed(2) + ' F';
// → "73.40 F"
Delaying
function calls
The delay()
function is used to execute a given callback
function after the given
number of milliseconds has elapsed.This actually works the same way as the
built - in setTimeout()
function does.This is shown in the following code:
function poll() {
if (++cnt < max) {
console.log('polling round ' + (cnt + 1));
timer = _.delay(poll, interval);
} else {
clearTimeout(timer);
}
}
var cnt = -1,
max = 5,
interval = 3000,
timer;
poll();
// →
// polling round 1
// polling round 2
// polling round 3
// polling round 4
// polling round 5
function bytes(b) {
var units = ['B', 'K', 'M', 'G', 'T', 'P'],
target = 0;
while (b >= 1024) {
b = b / 1024;
target++;
}
return (b % 1 === 0 ? b : b.toFixed(1)) +
units[target] + (target === 0 ? '' : 'B');
}
var collection = [
1024,
1048576,
345198,
120120120
];
_.map(collection, bytes);
// → [ "1KB", "1MB", "337.1KB", "114.6MB" ]
The bytes()
function takes a numerical argument, which is the number of bytes
to be formatted.This is the starting unit.We just keep incrementing the target
unit until we have something that is less than 1024. For example, the last item in
our collection maps to '114.6MB'.The bytes()
function can be passed directly
to map() since it 's expecting values in our collection as they are.
var object = {
first: 'Roxanne',
last: 'Elliot',
name: function() {
return this.first + ' ' + this.last;
},
age: 38,
retirement: 65,
working: function() {
return this.retirement - this.age;
}
};
_.map(object, function(value, key) {
var item = {};
item[key] = _.isFunction(value) ? object[key]() : value
return item;
});
// →
// [
// { first: "Roxanne" },
// { last: "Elliot" },
// { name: "Roxanne Elliot" },
// { age: 38 },
// { retirement: 65 },
// { working: 27 }
// ]
_.map(object, function(value, key) {
var item = {};
item[key] = _.result(object, key);
return item;
});
// →
// [
// { first: "Roxanne" },
// { last: "Elliot" },
// { name: "Roxanne Elliot" },
// { age: 38 },
// { retirement: 65 },
// { working: 27 }
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment