Useful Lodash utilities

In this post, you can find a collection of the most useful lodash utilities. Each method has a quick description, its signature, and examples on how to use it. It also has links to the documentation, the weekly downloads (NPM), and the bundle size from bundlephobia.

The goal here is to list as many methods as possible, in the least possible space. I tried not to include methods that are identical to their native counterparts unless they are popular. You can’t say that this post is something new or groundbreaking, but you can rather see it as a resource that will remind/introduce you to cool lodash methods quickly. To give you an idea, most of the examples are from the lodash documentation. The descriptions, on the other hand, are custom because I wanted to provide clear, non-technical explanations (at least I tried). Finally, I assume that you download the individual packages, not the whole library. As a result, you won’t see me calling the methods with the underscore e.g. _.map() but directly e.g. map().

* You’ll see that many functions have a parameter named iteratee with a default value of identity. Both of them are helper lodash functions listed under the Utils section. Click the links above to learn more.

Table of contents

Array

Methods to chunk, flatten, and perform set operations on arrays.

chunk chunk(array, chunkSize = 1)

Split an array into equal chunks. Docs - weekly downloads - size.

chunk(["a", "b", "c", "d"], 2); // [["a", "b"], ["c", "d"]]
chunk(["a", "b", "c", "d"], 3); // [["a", "b", "c"], ["d"]]

flatten - flattenDeep - flattenDepth

const nested = [1, [2, [3, [4]], 5]];
flatten(nested); // [1, 2, [3, [4]], 5]
flattenDeep(nested); // [1, 2, 3, 4, 5]
flattenDepth(nested, 1); // [1, 2, [3, [4]], 5]
flattenDepth(nested, 2); // [1, 2, 3, [4], 5]

compact compact(array)

Remove the falsey values. Docs - weekly downloads - size.

compact([0, 1, false, 2, "", 3]); // [1, 2, 3]

Sets

I think it’s easier to understand what each method does in this section by looking at the pictures, rather than reading the descriptions. Set images are from Wikipedia.

Set methods in lodash
Set methods in lodash

union

  • union(arrays): Create an array that holds all the unique values from both arrays. Docs - weekly downloads - size.
  • unionBy(arrays, iteratee = identity): It’s like union, but you can specify the unique criterion. Docs - weekly downloads - size.
union([2, 1], [2, 3]); // [2, 1, 3]
unionBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], "x"); // [{ x: 1 }, { x: 2 }]

intersection

  • intersection(arrays): Create an array that holds only the unique values that exist in both arrays. Docs - weekly downloads - size.
  • intersectionBy(arrays, iteratee = identity): It’s like intersection, but you can specify the unique criterion. Docs - weekly downloads - size.
intersection([2, 1], [2, 3]); // [2]
intersectionBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], "x"); // [{ x: 1 }]

difference

  • difference(arrays): Create an array that holds the unique values that exist in the first array but not in the second. Docs - weekly downloads - size.
  • differenceBy(arrays, iteratee = identity): It’s like difference, but you can specify the unique criterion. Docs - weekly downloads - size.
difference([2, 1], [2, 3]); // [1]
differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], "x"); // [{ x: 2 }]

See also without if you prefer to pass values instead of a second array.

xor

  • xor(arrays): Create an array that holds the unique values from both arrays but not those that exist in both. Docs - weekly downloads - size.
  • xorBy(arrays, iteratee = identity): It’s like xor, but you can specify the unique criterion. Docs - weekly downloads - size.
xor([2, 1], [2, 3]); // [1, 3]
xorBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], "x"); // [{ x: 2 }]

zip zip(arrays)

Group arrays, see the example. Docs - weekly downloads - size.

zip(["a", "b"], [1, 2], [true, false]); // [["a", 1, true], ["b", 2, false]]// edge cases:
zip(["a", "b"], [1, 2, 3]); // [["a", 1], ["b", 2], [undefined, 3]], different length.
zip(["a", "b"]); // [["a"], ["b"]], single array.

Collection

You can use these methods for array-like objects and regular objects.

Example users array

I will reference this array in some examples.

const users = [
  { name: "barney", age: 36, active: false },
  { name: "fred", age: 40, active: true },
  { name: "pebbles", age: 1, active: false }
];

countBy countBy(collection, iteratee = identity)

Count the different values. Docs - weekly downloads - size.

countBy([1, 1, 2]); // { "1": 2, "2": 1 }countBy([6.1, 4.2, 6.3], Math.floor); // { "4": 1, "6": 2 }
countBy(["one", "two", "three"], "length"); // { "3": 2, "5": 1 }
// 2 items with length 3, 1 item with length 5.
countBy(users, "active"); // { false: 2, true: 1 }, by an object property.

groupBy groupBy(collection, iteratee = identity)

Group the different values. Example users array - Docs - weekly downloads - size.

groupBy([6.1, 4.2, 6.3], Math.floor); // { "4": [4.2], "6": [6.1, 6.3] }groupBy(["one", "two", "three"], "length"); // { "3": ["one", "two"], "5": ["three"] }
groupBy(users, "active"); // { "false": [barney, pebbles], "true": [fred] }

orderBy orderBy(collection, iteratees = identity, [order])

Sort the collection by a property. It’s like sortBy, but you can also set the order (asc/desc). Example users array - Docs - weekly downloads - size.

orderBy(users, "age", "desc"); // [fred (40), barney (36)], pebbles (1)]orderBy(users, ["name", "age"], ["asc", "desc"]); // by name and then (if equal) by age.
// [barney("b",36), fred("f",40), pebbles("p",1)]

partition partition(collection, predicate = identity)

Split the collection into two groups. Example users array - Docs - weekly downloads - size.

partition(users, o => o.active); // [[fred(true)], [barney(false), pebbles(false)]], func.
partition(users, { age: 1, active: false }); // [[pebbles], [barney, fred]], object.
partition(users, ["active", false]); // [[barney, pebbles], [fred]], array.
partition(users, "active"); // [[fred], [barney, pebbles]], string.

These methods are useful if you want to support Internet Explorer—instead of relying on polyfills and native methods—or if you want to use the iteratee shorthand. It has been also reported in the past that they are more performant than the native methods, but I can’t find a definitive resource for that.

  1. includes(collection, value, fromIndex = 0) Docs - weekly downloads - size
  2. each/foreach(collection, iteratee = identity) Docs - weekly downloads - size
  3. map(collection, iteratee = identity) Docs - weekly downloads - size
  4. filter(collection, predicate= identity) Docs - weekly downloads - size
  5. find(collection, predicate = identity, fromIndex = 0) Docs - weekly downloads - size
  6. reduce(collection, iteratee = identity, accumulator) Docs - weekly downloads - size

Example users array

/* 1 */ includes([1, 2, 3], 1); // true
/* 2 */ forEach([1, 2], value => console.log(value)); // Logs 1 then 2.
/* 3 */ map([4, 8], n => n * 2); // [8, 16]
/* 3 */ map(users, "name"); // ["barney", "fred", "pebbles"]
/* 4 */ filter(users, { age: 36, active: false }); // [barney]
/* 4 */ filter(users, ["active", false]); // [barney, pebbles]
/* 5 */ find(users, ["active", false]); // [barney], the first it finds.
/* 6 */ reduce([1, 2, 3], (acc, next) => (acc += next), 0); // 6

Other collection methods

shuffle([1, 2, 3, 4]); // [4, 1, 3, 2]
sample([1, 2, 3, 4]); // 2, get a random element.

Math

These are three of the most interesting lodash math methods.

maxBy maxBy(array, iteratee = identity)

Find the max in an array. Example users array - Docs - weekly downloads - size.

maxBy(users, o => o.age); // fred object, function
maxBy(users, "age"); // fred object, shorthand

minBy minBy(array, iteratee = identity)

Find the min in an array. Example users array - Docs - weekly downloads - size.

minBy(users, o => o.age); // pebbles object, function
minBy(users, "age"); // pebbles object, shorthand

round round(number, precision = 0)

Math.floor and Math.ceil in a single method. Docs - weekly downloads - size.

round(4.006); // 4
round(4.006, 2); // 4.01
round(4.004, 2); // 4
round(4060, -2); // 4100, 60 > 50 (middle with 2 precision)
round(4060, -3); // 4000, 60 < 500 (middle with 3 precision)

String

Methods to truncate, pad, escape characters, and change the casing of a string.

truncate truncate(string, { length = 30, ommission = "...", [separator]})

Shorten a string to a certain length. Docs - weekly downloads - size.

const body = "hi-diddly-ho there, neighborino";
truncate(body); // "hi-diddly-ho there, neighbo..."truncate(body, { length: 24, separator: " " }); // "hi-diddly-ho there,..."
truncate(body, { length: 24, separator: /,? +/ }); // "hi-diddly-ho there..."
truncate(body, { omission: " [...]" }); // "hi-diddly-ho there, neig [...]"

pad pad(string, length = 0, padCharacters)

Adds a character before and after the string. See also padStart and padEnd. Docs - weekly downloads - size.

pad("hello", 10); // "  hello   ", 2 spaces at the start, 3 at the end.
pad("mark", 10, "_"); // "___mark___"padStart(342, 10, "."); // ".......342", useful in CLI tools.
padStart(12, 10, "."); //  "........12"

deburr deburr(string)

Removes combining diacritical marks, and converts Latin-1 Supplement and Latin Extended-A letters to basic Latin letters. Docs - weekly downloads - size.

deburr("déjà vu"); // "deja vu"

escape - unescape

  • escape(string): Convert & < > '' ' to &amp; &lt; &gt; &quot; &#39;. In other words, convert characters to HTML entities. Docs - weekly downloads - size.
  • unescape(string): The opposite of escape, convert HTML entities to characters. Docs - weekly downloads - size.
escape("fred, barney, & pebbles"); // "fred, barney, &amp; pebbles"
unescape("fred, barney, &amp; pebbles"); // "fred, barney, & pebbles"

See also escapeRegExp.

capitalize capitalize(string)

Capitalize a string. Docs - weekly downloads - size.

capitalize("mark"); // Mark
capitalize("MARK"); // Mark
capitalize("mArK"); // Mark

Change letter casing

camelCase("--foo-bar--"); // "fooBar"
camelCase("__FOO_BAR__"); // "fooBar"
camelCase("Foo Bar"); // "fooBar"kebabCase("Foo Bar"); // "foo-bar"snakeCase("Foo Bar"); // "foo_bar"startCase("Foo Bar"); // "Foo Bar"

Util

General functional programming utilities.

identity and iteratee

  • identity(value): A helper utility that returns the first value it receives. It’s used as a default value for the iteratee in many methods. It’s like the constant method. Docs - weekly downloads - size
  • iteratee(func = identity): Creates a new function. If the func argument is a function, the new function calls func with the arguments it receives—doesn’t sound really useful. When func is a property name (e.g. user.age), though, the new function returns the value for that property. Finally, if func is an array or object, it returns true for elements that have those properties, or false otherwise. Consider the examples below. Docs - weekly downloads - size
const users = [
  { name: "barney", age: 36, active: true },
  { name: "fred", age: 40, active: false }
];
identity(users[0]) === users[0]; // truefilter(users, iteratee({ name: "barney" })); // [barney], `matches` shorthand.
filter(users, iteratee(["name", "fred"])); // [fred], `matchesProperty` shorthand.map(users, iteratee("name")); // ["barney", "fred"], `property` shorthand.

See also matches, matchesProperty, property, and method.

bindAll bindAll(object, methodNames)

For the object methods you choose, this inside them points to the object, no matter how they’re called. Docs - weekly downloads - size

const view = {
  label: "docs",
  click: function() {
    console.log("clicked " + this.label);
  }
};
bindAll(view, ["click"]);element.addEventListener("click", view.click); // "clicked docs"

flow flow(funcs)

Chain functions from left to right. See also flowRight. Docs - weekly downloads - size

const add = (a, b) => a + b;
const square = n => n * n;
const addSquare = flow([add, square]); // or flow(add, square) if you prefer.addSquare(1, 2); // returns 9. Returns 3 after `add` and 9 after `square`

Lodash FP and currying can make flow even more interesting.

times times(n, iteratee = identity)

Call the iteratee n times—passing the current index each time—and return an array with the results. Docs - weekly downloads - size

times(3); // [0, 1, 2]times(3, String); // ["0", "1", "2"]
times(4, constant(0)); // [0, 0, 0, 0]

range - rangeRight

Create an array where you specify the start, end, and the step. The start number is optional.

range(4); // [0, 1, 2, 3]range(-4); // [0, -1, -2, -3]
range(0, 20, 5); // [0, 5, 10, 15]
range(1, 4, 0); // [1, 1, 1]
range(0); // []
rangeRight(4); // [3, 2, 1, 0]rangeRight(-4); // [-3, -2, -1, 0]

Other interesting methods

  1. cond(pairs) Ιt’s like a switch statement, but you create it on the fly by composing functions. See also toPairs. Docs - weekly downloads - size
  2. conforms(source) Similar to the object shorthand of the iteratee but you can specify the operation. Docs - weekly downloads - size
  3. noop() A method that returns undefined. Useful when you want to call a method no matter what, without getting an error. See the example. Docs - weekly downloads - size
  4. uniqueId(prefix = '') Generate a unique ID with an optional prefix. Docs - weekly downloads - size
  5. over(iteratees = identity) Docs - weekly downloads - size

Example users array

const func = cond([  [matches({ a: 1 }), /* . */ constant("matches A")],
  [conforms({ b: isNumber }), constant("matches B")],
  [stubTrue, /* .......... */ constant("no match")]
]);
/* 1 */ func({ a: 1, b: 2 }); // "matches A"
/* 1 */ func({ a: 0, b: 1 }); // "matches B"
/* 1 */ func({ a: "1", b: "2" }); // "no match"
/* 2 */ filter(users, conforms({ age: n => n > 1, active: n => n === true })); // [fred]
/* 3 */ times(2, noop); // [undefined, undefined]
/* 3 */ const callbackThatMustBeCalled = userCallback || noop;
/* 4 */ uniqueId(); // "105"
/* 4 */ uniqueId("contact_"); // "contact_104"
/* 5 */ const func = over([Math.max, Math.min]);
/* 5 */ func(1, 2, 3, 4); // [4, 1]

Lang

Utilities that simplify cloning objects, type checking, and type conversions.

clone clone(value)

Create a shallow copy of a value. Docs - weekly downloads - size

const objects = [{ a: 1 }, { b: 2 }];
const shallow = clone(objects);shallow[0] === objects[0]; // true

cloneDeep cloneDeep(value)

Create a deep copy of a value. Docs - weekly downloads - size

const objects = [{ a: 1 }, { b: 2 }];
const deep = cloneDeep(objects);deep[0] === objects[0]; // false

isEqual isEqual(value, other)

Deep comparison between two values. Docs - weekly downloads - size

const object = { a: 1 };
const other = { a: 1 };
isEqual(object, other); // trueobject === other; // false

isEmpty isEmpty(value)

Check if a value is empty. For array-like: length is 0; for objects: no own properties; for maps/sets: size is 0. Docs - weekly downloads - size

isEmpty(null); // true
isEmpty(true); // true
isEmpty(1); // true
isEmpty([1, 2, 3]); // false
isEmpty({ a: 1 }); // false

Type-checking

isNumber isNumber(value)

Check if the value is a Number primitive or object. Docs - weekly downloads - size

isNumber(3); // trueisNumber(Number.MIN_VALUE); // true
isNumber(Infinity); // true
isNumber("3"); // false

isInteger isInteger(value)

Docs - weekly downloads - size

isInteger(3); // trueisInteger(Number.MIN_VALUE); // false
isInteger(Infinity); // false
isInteger("3"); // false
isInteger(3.2); // false

isPlainObject isPlainObject(value) {isPlainObject}

Checks if value is a plain object, that is, an object created by the Object constructor or one with a [[Prototype]] of null. Docs - weekly downloads - size

function Foo() {
  this.a = 1;
}
isPlainObject(new Foo()); // falseisPlainObject([1, 2, 3]); // false
isPlainObject({ x: 0, y: 0 }); // trueisPlainObject(Object.create(null)); // true

isMatch isMatch(object, source)

See if the object has a property with that value. If you partially apply the source, it’s the same as matches. Docs - weekly downloads - size

const object = { a: 1, b: 2 };
isMatch(object, { b: 2 }); // trueisMatch(object, { b: 1 }); // false

Other interesting type-checking methods

isArray, isArrayLike, isBoolean, isDate, isElement, isError, isFinite, isFunction, isNative, isObject, isObjectLike, isRegExp, isSafeInteger, isString, isTypedArray, and isUndefined.

Type conversion methods

toArray, toFinite, toInteger, toLength, toNumber, toPlainObject, toSafeInteger, and toString.

Seq

Chain lodash methods with the Sequence methods. An alternative is the flow method.

Example users array

const youngest = _.chain(users) // start the chain by passing a value.  .sortBy("age")
  .map(o => `${o.name} is ${o.age}`)
  .head() // get the first element from the array.
  .value(); // "pebbles is 1", get the value.

See also tap, thru. They both intercept the chain and can modify the value, but thru returns the value.

Object

Methods that simplify object creation, validation, getting/setting properties, and more.

get - set

const object = { a: [{ b: { c: 3 } }] };
get(object, "a[0].b.c"); // 3get(object, ["a", "0", "b", "c"]); // 3
get(object, "a.b.c", "default"); // 'default'
set(object, "a[0].b.c", 4); // { a: [{ b: { c: 4 } }] }set(object, ["x", "0", "y", "z"], 5); // { a: [{ b: { c: 4 } }], x: [{ y: { z: 5 } }] }

See also unset

has - hasIn

  • has(object, property): Check if the object directly has the property, excluding inherited methods. Docs - weekly downloads - size
  • hasIn(object, property): Check if the object has the property, directly or by inheritance. Docs - weekly downloads - size
const object = { a: { b: 2 } };
const other = Object.create({ a: Object.create({ b: 2 }) }); // inheritance
has(object, "a"); // truehas(object, "a.b"); // true
has(object, ["a", "b"]); // true
has(other, "a"); // false, a is not a direct property of other.
hasIn(other, "a"); // true

assign - assignIn

  • assign(object, otherObjects): Assign the own properties of the otherObjects to the object. Docs - weekly downloads - size
  • assignIn(object, otherObjects): Assign the own and inherited properties of the otherObjects to the object. Docs - weekly downloads - size
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({ a: 0 }, new Foo(), new Bar()); // { a: 1, b: 2, c: 3, d: 4 }

merge merge()

const object = { a: [{ b: 2 }, { d: 4 }] };
const otherr = { a: [{ c: 3 }, { e: 5 }] };
merge(object, otherr); //    { a: [{ b: 2, c: 3 }, { d: 4, e: 5 }] }assignIn(object, otherr); // { a: [{ c: 3 }, { e: 5 }] }
assign(object, otherr); //   { a: [{ c: 3 }, { e: 5 }] }

defaults - defaultsDeep

  • defaults(object, defaultProperties): If the object doesn’t already have the default properties, add them. Docs - weekly downloads - size
  • defaultsDeep(object, defaultProperties): Same as defaults but recursively for nested properties too. Docs - weekly downloads - size
defaults({ a: 1 }, { b: 2 }, { a: 3 }); // { a: 1, b: 2 }
defaultsDeep({ a: { b: 2 } }, { a: { b: 1, c: 3 } }); // { a: { b: 2, c: 3 } }

pick - omit

  • pick(object, properties): Create an object and choose the properties you want. Docs - weekly downloads - size
  • omit(object, properties): Create an object and choose what properties you don’t want. (It’s slower than pick) Docs - weekly downloads - size
const object = { a: 1, b: "2", c: 3 };
pick(object, ["a", "c"]); // { a: 1, c: 3 }
omit(object, ["a", "c"]); // { b: "2" }

toPairs - fromPairs

  • toPairs(object): Put the object’s own key-value pairs in an array. Each entry (pair) is also an array. Docs - weekly downloads - size.
  • fromPairs(pairs): The opposite from toPairs (this is an Array method by the way). Docs - weekly downloads - size.
toPairs({ a: 1, b: 2 }); // [["a", 1], ["b", 2]]
fromPairs([["a", 1], ["b", 2]]); // { a: 1, b: 2 }

keys - values

function Foo() {
  this.a = 1;
  this.b = 2;
}
Foo.prototype.c = 3;
keys(new Foo()); // ["a", "b"]keys("hi"); // ["0", "1"]
keysIn(new Foo()); // ["a", "b", "c"]
values(new Foo()); // [1, 2]values("hi"); // ["h", "i"]
valuesIn(new Foo()); //  [1, 2, 3]

Other object methods

  1. create(prototype, optionalProperties) Create an object that inherits from (is linked to) the prototype object (first parameter). Docs - weekly downloads - size
  2. invert(object) Invert the object’s key-value pairs. Docs - weekly downloads - size
  3. invoke(object, property, [args]) Invoke an object’s method. You can also pass arguments. Docs - weekly downloads - size
  4. mapValues(object, iteratee = identity) Docs - weekly downloads - size
  5. transform Similar to reduce but you can stop the iteration by returning false. Docs - weekly downloads - size

Example users array

/* 2 */ const object = { a: 1, b: 2, c: 1 };
/* 2 */ invert(object); // { 1: "c", 2: "b" }/* 3 */ const object = { a: [{ b: { c: [1, 2, 3, 4] } }] };
/* 3 */ invoke(object, "a[0].b.c.slice", 1, 3); // [2, 3]/* 4 */ mapValues(users, "age"); // { '0': 36, '1': 40, '2': 1 }/* 5 */ transform(
  [2, 3, 4],
  function(result, n) {
    result.push((n *= n));
    return n % 2 == 0;
  },
  []
); //  [4, 9]

Function

Methods that moderate the execution of functions and some that help with functional programming like currying/partial application.

debounce - throttle

  • debounce(func, wait = 0, options={ leading = false, trailing = true }): Groups the func calls if they happen in less than wait ms (imagine the calls as a sudden burst of events). By “group” I mean it doesn’t allow the func to execute. Docs - weekly downloads - size
  • throttle(func, wait = 0, options={ leading = true, trailing = true }): Calls the func at most every wait ms. Unlike debounce, throttle guarantees the execution of the func at least every wait ms. Docs - weekly downloads - size

True for both of them: If leading and trailing options are true, func is called on the trailing edge of the timeout only if the function is called more than once during the wait timeout.

function scrollHandler(e) {
  console.log("Scrolling and rolling");
}
const debounced = _.debounce(scrollHandler, 600);
const throttled = _.throttle(scrollHandler, 600);
document.addEventListener("scroll", scrollHandler); // regular
// document.addEventListener("scroll", debounced); // debounce
// document.addEventListener("scroll", throttled); // throttle

once once(func)

Create a function that can be called only once. Docs - weekly downloads - size

const initialize = once(createApplication);
initialize(); // `createApplication` is invoked
initialize(); // // `createApplication` is not invoked

curry - partial

A blog post that explains the differences between them: Currying versus partial application (with JavaScript code)

const regularFunction = (a, b, c) => [a, b, c];
const curried = curry(regularFunction); // notice that we don't pass a parameter here.curried(1)(2)(3); // [1, 2, 3]curried(1, 2)(3); // [1, 2, 3]
curried(1, 2, 3); // [1, 2, 3]
const partiallyApplied = partial(regularFunction, 1, 2); // here we apply 2 parameters.partiallyApplied(3); // [1, 2, 3]

bind bind(func, this, optionalArgs)

Create a function with your preferred this binding, and optionally pass (or apply) arguments to that function. The difference with Function.prototype.bind is that it doesn’t set the length property. Docs - weekly downloads - size

function greet(greeting, punctuation) {
  return greeting + " " + this.name + punctuation;
}
const user = { name: "fred" };
const bound = bind(greet, user, "hi");bound("!"); // "hi fred!"

Other function methods

  1. memoize(func, [resolver]) Cache the result of a function. Docs - weekly downloads - size
  2. unary(func) Creates a function that accepts only one argument. Docs - weekly downloads - size
  3. wrap(value, wrapper = identity) Wrap a function within another function (kind of). Docs - weekly downloads - size
  4. after(n, func) Creates a function that invokes func only after the function has been called n times. Docs - weekly downloads - size
  5. before(n, func) Creates a function that can be called at most n - 1 times. Docs - weekly downloads - size
/* 2 */ map(["6", "8", "10"], unary(parseInt)); // [6, 8, 10], what you'd expect
/* 2 */ ["6", "8", "10"].map(parseInt); // [6, NaN, 2], wrong because parseInt(string, radix)
/* 3 */ const p = _.wrap(
  _.escape,
  (func, text) => `<p> + ${func(text)} + </p>`
);
/* 3 */ p("fred, barney, & pebbles"); // "<p>fred, barney, &amp; pebbles</p>"
/* 4 and 5*/
const saves = ["profile", "settings", "messages"];
const logTheFirst2 = _.before(saves.length, function() {  console.log("Logging the save.");
});
const completionMessage = _.after(saves.length, function() {  console.log("Done saving!");
});
_.forEach(saves, function() {
  console.log("Doing other things.");
  logTheFirst2();
  completionMessage();
});
// Doing other things. (1)
// Logging the save.
// Doing other things. (2)
// Logging the save.
// Doing other things. (3)
// Done saving!

* If you like the format, I have similar posts for the Array.prototype and the String.prototype.

Other things to read

Popular notes

Other posts