Since its debut in 2012 as an upgrade to the aging Underscore.js library, Lodash has become an integral part of JavaScript development across the ecosystem. Its meticulously crafted utility belt has earned the trust of over 19,000 GitHub stargazers and clocked over 5M weekly npm downloads – a true open-source juggernaut!
So what does Lodash actually do? In short, it focuses on common operations involving collections like arrays, objects maps and so on. This includes:
- Iteration utilities –
forEach
,map
,reduce
etc. - Function manipulation –
debounce
,memoize
,once
etc. - Object/array manipulation –
merge
,omit
,fill
etc. - Condition checking –
isEqual
,isEmpty
,startsWith
etc.
With the rise of Array.prototype
methods and other native improvements however, is something like Lodash still relevant in modern JavaScript?
Absolutely!
While native methods handle basic use cases well (mapping, filtering etc.), Lodash shines for more complex flows involving objects, function manipulation, error handling, default values and so on. Its methods are also battle-hardened across browsers and environments in ways newly introduced native functionality takes time to mature.
Let‘s now explore 10 particularly indispensable Lodash functions you should have in your JavaScript toolkit with examples of them in action.
1. cloneDeep() – Deep Clone Complex Objects & Arrays
The cloneDeep()
method creates full recursive copies of the source input:
const obj = {foo: {bar: ‘baz‘}};
const cloned = _.cloneDeep(obj);
cloned.foo.bar = ‘qux‘;
console.log(obj); // {foo: {bar: ‘baz‘}} - unchanged
This reliably handles edge cases like cloning prototypes, functions, typed arrays and other complex data types safer than alternatives like JSON.parse(JSON.stringify(obj))
.
Use when you need distinct instances of data structures like configs or mutable documents:
let appState = {/*...*/};
function updateState(changes) {
appState = _.cloneDeep(appState);
// work safely without side effects
}
Performance: Roughly on par with native JSON cloning for smaller objects, but faster than simplistic recursive alternatives.
2. uniq() / uniqBy() – Remove Duplicate Values
Ever needed to extract just unique elements from an array? Say hello to uniq()
and uniqBy()
:
_.uniq([1, 1, 2, 3]); // [1, 2, 3]
interface Person {age: number;}
const people: Person[] = [
{age: 20},
{age: 20},
{age: 21}
];
_.uniqBy(people, ‘age‘);
// [{age: 20}, {age: 21}]
The first filters by reference equality, while uniqBy()
lets you de-dupe by property.
This capability comes in handy when normalizing messy datasets from external sources or eliminating noise for analysis purposes.
Performance: Roughly on par with native Set
based approaches in microbenchmarks.
3. merge() – Merge Objects
Manually merging objects in native JavaScript gets messy fast:
const merged = Object.assign({}, obj1, obj2);
// also need to concat arrays separately...
Lodash‘s merge()
handles this recursively without mutating source objects:
const merged = _.merge({}, obj1, obj2);
It joins array properties appropriately and handles same-named keys intelligently:
const merged = _.merge(
{fruits: [‘apple‘]},
{fruits: [‘banana‘]},
);
console.log(merged);
// {fruits: [‘apple‘, ‘banana‘]}
Use when assembling options objects from multiple sources:
function connect(config) {
config = _.merge(
defaults,
userConfig,
privatedDefaults
);
// ...
}
Performance: Roughly on par with Object.assign()
on modern engines.
4. get() – Safer Nested Value Access
Accessing nested properties in JavaScript objects can lead to pesky runtime errors:
const user = {}; // could come from 3rd party code
function getAddress() {
return user.address.street; // throws if !user.address
}
Lodash‘s get()
avoids this by letting you return a default fallback if the path doesn‘t exist:
const street = _.get(user, ‘address.street‘, ‘N/A‘);
// ‘N/A‘
Think of get()
like a null-safe and exception-safe navigator for complex objects & arrays.
Performance: Roughly 3-4x faster than native nullish coalescing alternatives as per jsBench tests.
5. chunk() – Split Arrays into Smaller Groups
When working with long arrays, it‘s often handy to split into smaller fixed-length chunks:
const myArray = [1, 2, 3, 4, 5, 6];
_.chunk(myArray, 2);
// [[1, 2], [3, 4], [5, 6]]
This allows iterating over batches of elements at a time, or "pagination" when needing to cap chunk size.
Use Cases:
- Paginate array for display in chunks
- Split up work into async batches
- Group form fields into rows/columns
Performance: Falls behind native approaches using .slice()
in benchmarks.
6. shuffle() – Randomize Array Element Order
Getting a random sampling from an array is simple with shuffle()
:
const list = [1, 2, 3];
_.shuffle(list);
// [2, 3, 1] - randomized
The shuffled result retains all elements, just in random order.
You can also request a small sample:
_.sampleSize(list, 2); // [3, 1] - extract random 2 elements
Use cases:
- Shuffle song playback order
- Randomize test case order
- Select random newsletter giveaway winners
Performance: Roughly on par with native Fisher-Yates shuffles.
7. debounce() / throttle() – Limit Function Call Rate
debounce()
and throttle()
help optimize expensive function calls by limiting invocation rate.
debounce()
ensures a function is only actually invoked after a certain amount of idle time has elapsed:
let expensiveFn = () => {
// Complex logic
};
const debounced = _.debounce(expensiveFn, 500);
document.addEventListener(‘input‘, debounced);
Now expensiveFn()
will only trigger 500ms after typing stops. Useful for implementing search boxes where you only care about the final state.
Meanwhile, throttle()
enforces a maximum number of times a function can run per unit time:
let throttledLogger = _.throttle(logMessage, 3000); // max once per 3s
window.addEventListener(‘mousemove‘, throttledLogger);
This is useful for reigning in potentially hyperactive event callbacks that could fire continuously at high frequency otherwise.
8. omit() – Filter Out Object Keys
The opposite of .pick()
, omit()
returns a copy of an object with certain keys filtered out:
_.omit({name: ‘John‘, age: 40}, ‘age‘);
// {name: ‘John‘}
Use cases:
- Remove unused/temporary fields from API response
- Filter sensitive fields from configs and logs
Omitting fields helps narrow down objects to just what‘s needed for the next process.
9. sample() / sampleSize()- Fetch Random Elements
When you need one or more random elements picked from an array, use sample()
and sampleSize()
:
const colors = [‘red‘, ‘blue‘, ‘green‘];
_.sample(colors); // ‘green‘ - single random element
_.sampleSize(colors, 2); // [‘red‘, ‘blue‘] - random 2 elements
Handy for:
- Select random featured products
- Shuffle up suggested playlist
- Choose winners fromgiveaway applicants
10. chain() – Sequence Function Calls
Lodash‘s chain()
allows piping a series of data transformations:
const orders = _.chain([
{amount: 100},
{amount: 230},
])
.filter(o => o.amount > 200)
.map(‘amount‘)
.sum()
.value();
// Total amount from filtered high value orders
// e.g. database aggregations
Calls in between chain()
are deferred until .value()
is called, avoiding intermediate array allocations.
Bonus: zip() & unzip() – Array Grouping
While less common, zip()
and unzip()
provide a unique form of array manipulation by grouping similar index elements across multiple arrays:
const keys = [‘name‘, ‘age‘];
const values = [‘John‘, 30];
_.zip(keys, values);
// [[‘name‘, ‘John‘], [‘age‘, 30]]
_.unzip([[‘name‘, ‘John‘], [‘age‘, 30]]);
// [[‘name‘, ‘age‘], [‘John‘, 30]]
You can think of zip()
as packing rows into key-value pairs, while unzip()
is the inverse operation.
Use cases are relatively rare, but can help convert array "columns" to object properties and vice versa.
When to Reach for Lodash Over Native Methods
While modern JavaScript has come a long way with native array utilities like .map()
, .filter()
etc., Lodash opens up way more possibilities specifically when working with plain objects, functions, and nested structures.
Some common cases where Lodash excels include:
- Complex object manipulation flows –
merge
,omit
,pick
,defaults
etc. - Abstracting callback hysteresis with
debounce
andthrottle
- Safer value accessing with
get
and related utilities - Advanced iteration methods like
partition
,chunk
etc. - Outputting method chains for cleaner code with
chain
So while you should generally prefer built-in methods for simpler cases, don‘t hesitate to pull in Lodash when you need more sophisticated functionalities.
Complementary JavaScript Utilities
While Lodash covers collection manipulation and other common utilities well, being focused on that domain means some gaps like date handling that are addressed neatly by complementary libraries:
date-fns – Modern date utility toolbelt. All Lodash goodness, but for JavaScript dates!
Immer – Immutable state management with normal mutative syntax. Complements Lodash for reducers and related state manipulation.
lodash-id – Adds useful ID generation functions missing in Lodash like GUIDs and nanoid.
So explore adding such focused utilities to cover functionality gaps compared to a "do-everything" utils toolkit.
Performance Tradeoffs to Keep in Mind
While Lodash sports an extensive and well-optimized codebase, certain methods can get expensive when working with giant collections of data.
Some particular cases to keep an eye out for:
cloneDeep()
– Recursive traversal has a performance cost with nested structures. Keep cloned data lean.debounce()
/throttle
– Timer handling introduces slight overhead. Use wisely.union
/difference
etc – Comparisons can get costly for thousands of elements.
In such cases, you may want to fall back to simpler native alternatives that leverage engine optimizations like Set
operations and array routines.
Real-World Lodash Success Stories
With over 19,000 GitHub stars and 5 million+ weekly downloads, Lodash clearly serves many projects and users well. But a sampling of well-known products leveraging it heavily brings home just how battle-tested and reliable it has become:
- Adobe Photoshop – Powering several editor components with Lodash as a dependency
- Google Analytics – Extensive usage within GA Google Tag Manager frontend
- VSCode – Helper utilities like file watching built using Lodash
- Yelp – Data manipulation involving JSON responses powered by Lodash
Indeed, JavaScript engineers at leading global software companies use Lodash utilities extensively in shipping production code.
Time to Level Up Your Skills!
I hope walking through practical examples of these 10 important Lodash functions gives you fresh ideas on how it can help create cleaner application code in your own projects.
Lodash has a ton more handy utilities in store like sortBy()
, groupBy()
, flattenDeep()
etc. so do invest time browsing the full documentation when you can. Identify frequent scenarios that are great fits for introducing Lodash polish moving forward.
Soon, you‘ll wonder how your libraries got by without lodash functions before!