As JavaScript developers, we often find ourselves reinventing the wheel by writing repetitive code for common data transformations and operations. Luckily, the venerable Lodash library has our back.
Lodash provides over 100 battle-tested utility functions out of the box to save us time and energy. With its modular methods, excellent performance, and focus on doing common things well, Lodash has become an essential part of every JavaScript developer‘s toolbox.
In this comprehensive guide, we‘ll dive deep into 10 of the most useful Lodash functions you should know, with plenty of examples and expert tips along the way.
Add these to your toolkit, and you‘ll write better JavaScript code. Let‘s get started!
1. cloneDeep – Escape Object Mutability Hell
Thanks to how JavaScript handles references, objects and arrays are mutable by default, often leading to unexpected side effects and bugs.
For example, making a copy with spread syntax or Object.assign()
only clones at the first level:
const obj = {
prop1: ‘value1‘,
prop2: {
subProp: ‘sub value‘
}
}
const shallowCopy = {...obj}
obj.prop2.subProp = ‘new sub value‘
// shallowCopy is mutated as well since obj was only shallow copied
The same problem appears when passing objects as arguments, returning values from functions, and more.
These shared references between the original object and its copies means they point to the same underlying memory. So changes to one also affect the other.
This mutability makes reasoning about state changes challenging. But fear not – Lodash‘s cloneDeep()
handles it for you:
const deepCopy = _.cloneDeep(obj)
obj.prop2.subProp = ‘new sub value‘
// deepCopy remains unchanged thanks to deep cloning
Under the hood, cloneDeep
recursively traverses and clones the full object structure into completely new objects with no shared references.
It works seamlessly on arrays, plain objects, Date objects, Maps, Sets, and more. Just pass anything you need cloned and out comes an independent copy.
cloneDeep
should be routinely used:
-
When initializing state in React and Vue apps to prevent mutation bugs.
-
Before returning any objects/arrays from functions to avoid callers mutating the originals unexpectedly.
-
Before passing objects as arguments to prevent callees affecting the caller‘s object unintentionally.
Get in the habit of cloning with cloneDeep
liberally, and you can avoid a whole class of nasty bugs.
Performance Tip: the cloning operation can be expensive for huge objects. In those cases, only clone what you actually need to avoid unnecessary overhead.
2. uniq / uniqBy – Filtering Duplicate Values
Removing duplicate values from an array is a common task required before further processing.
Lodash makes deduping easy with _.uniq
:
const array = [1, 2, 1, 3, 1, 4]
_.uniq(array) // [1, 2, 3, 4]
For arrays of objects, _.uniqBy
lets you filter by uniqueness on a certain property:
const items = [
{ id: 1, label: ‘a‘},
{ id: 2, label: ‘b‘ },
{ id: 1, label: ‘c‘}
]
_.uniqBy(items, ‘id‘)
// [{ id: 1, label: ‘a‘}, {id: 2, label: ‘b‘ }]
You can also pass a custom function to filter objects based on an arbitrary condition.
These methods are perfect for cleaning up data from APIs and other external sources where duplicates often appear.
According to npm download stats, uniq
and uniqBy
are among the most used Lodash functions as duplicate removal is so common.
Pro Tip: Since uniq
filters in-place, be sure to cloneDeep
first if the original array needs to be preserved.
3. merge – The Safe Object Merge
When combining objects from different sources, we often need to merge properties without losing existing data.
A naive approach with spread syntax leads to surprises:
const objA = {foo: ‘bar‘, options: {size: ‘large‘}}
const objB = {foo: ‘baz‘, options: {number: 42}}
const merged = {...objB, ...objA}
// {foo: ‘bar‘, options: {number: 42}}
Oops – objA.options
got completely overwritten instead of merged.
Lodash‘s _.merge()
carefully merges objects together preserving nested values:
const merged = _.merge(objA, objB)
// {
// foo: ‘bar‘
// options: {
// size: ‘large‘
// number: 42
// }
// }
Much better! Now options
contains keys from both objects properly merged.
Here are some great use cases for merge()
:
- Combining configuration objects from defaults + overrides
- Merging REST API responses client-side
- Safely assembling Redux state slices into a single state
- Merging CSS class names in React components
Performance Tip: merge
creates a new merged object each call. For merging many objects dynamically, consider mergeWith with an accumulator object instead.
4. groupBy – Split Arrays into Buckets
Often we need to partition data into groups or buckets based on a property value. Manually iterating and pushing to groups gets messy fast.
Lodash‘s _.groupBy()
handles this concisely:
const items = [
{ name: ‘Item 1‘, category: ‘A‘ },
{ name: ‘Item 2‘, category: ‘B‘ },
{ name: ‘Item 3‘, category: ‘B‘ },
{ name: ‘Item 4‘, category: ‘A‘ },
]
_.groupBy(items, ‘category‘)
/*
{
A: [
{name: ‘Item 1‘, category: ‘A‘},
{name: ‘Item 4‘, category: ‘A‘}
],
B: [
{name: ‘Item 2‘, category: ‘B‘},
{name: ‘Item 3‘, category: ‘B‘}
]
}
*/
The array is neatly organized into a results object with each key holding one group.
This transforms arrays into organized maps perfect for downstream display or processing.
Some example uses:
- Group products by category for navigation
- Bin analytics events by name/type
- Separate blog posts by year/month
Power-User Tip: Pass a function instead to group via custom logic:
_.groupBy(items, item => {
// complex group calculation...
})
5. map & mapKeys – Reshape Arrays & Objects
Transforming data structures between shapes is an everyday task. Lodash provides two excellent utilities for mapping arrays and objects into new forms.
First, _.map
allows remapping array elements into a new object with a different key and/or value shape:
const people = [
{ id: 1, name: ‘Fred‘ },
{ id: 2, name: ‘Sarah‘ }
]
_.map(people, ‘id‘)
// {
// 1: {id: 1, name: ‘Fred‘},
// 2: {id: 2, name: ‘Sarah‘}
// }
The array is transformed into an object with keys generated from each element‘s id.
For plain objects, _.mapKeys
reorganizes the keys while retaining values:
const roles = {
‘frontend‘: ‘JavaScript‘,
‘backend‘: ‘Go‘,
‘data science‘: ‘Python‘
}
_.mapKeys(roles, (value, key) => key.toUpperCase())
// {
// FRONTEND: ‘JavaScript‘,
// BACKEND: ‘Go‘,
// ‘DATA SCIENCE‘: ‘Python‘
// }
This allows flexible mapping from any object shape into another.
Clever use of map
and mapKeys
eliminates manual iteration and transformation code.
Extensibility Note: Pass a custom function to perform arbitrary conversion logic while mapping.
6. omit & pick – Filter Object Properties
When retrieving data objects from an external API, we usually need only a subset of the returned properties.
Manually deleting the unnecessary keys clutter the code. Lodash to the rescue with _.pick
and _.omit
!
_.pick
selects only needed keys:
const product = {
id: 42,
name: ‘Ice cream‘,
price: 4.99,
inventory: 10,
reviews: []
}
_.pick(product, [‘id‘, ‘name‘, ‘price‘])
// {id: 42, name: ‘Ice cream‘, price: 4.99}
While _.omit
does the inverse by filtering out unwanted keys:
_.omit(product, [‘reviews‘, ‘inventory‘])
// {id: 42, name: ‘Ice cream‘, price: 4.99}
Use these techniques to:
- Easily handle API payload pruning
- Filter object properties for logging
- Select subset of state needed in React Redux
- Pick CSV columns when parsing/converting
Both methods accept an array of keys or individual keys. And importantly, the original object remains unchanged for non-destructive filtering.
7. unzip – Invert Zip Arrays
In some cases, data arrives "zipped" together that needs to be separated back out:
const data = [
[‘fred‘, 30, ‘usa‘],
[‘sara‘, 24, ‘canada‘],
[‘bob‘, 40, ‘mexico‘]
]
We need to "unzip" this into separate arrays per column.
Lodash‘s _.unzip()
handles it elegantly:
_.unzip(data)
// [
// [‘fred‘, ‘sara‘, ‘bob‘],
// [30, 24, 40],
// [‘usa‘, ‘canada‘, ‘mexico‘]
// ]
The zipped rows unfold into discrete arrays per each position automatically.
Use cases include:
- Separating aggregated API data
- Splitting up CSV data for parsing
- Converting packed data into columns for charting
The inverse _.zip()
operation packs discrete arrays into zipped rows.
8. keyBy – Lookup Dictionaries from Arrays
APIs often return arrays of objects that need to be queried or searched by id or name.
We can build lookup dictionaries using _.keyBy()
:
const products = [
{id: 1, name: ‘Shirt‘},
{id: 2, name: ‘Pants‘},
{id: 3, name: ‘Hat‘}
];
_.keyBy(products, ‘id‘)
// {
// 1: {id: 1, name: ‘Shirt‘},
// 2: {id: 2, name: ‘Pants‘},
// 3: {id: 3, name: ‘Hat‘}
// }
Now products can be directly accessed by id instead of manually searching through the array each time.
Some handy uses:
- User profiles by id
- Product catalogs by SKU
- Cache query results by key
- Invert maps from arrays
This avoids messy nested loops and provides fast O(1) lookup speed.
9. unionWith – Merge Arrays via Rules
When combining arrays from multiple sources, we may need custom logic to merge elements.
For example, union based on object properties vs indexes:
const birds = [{id: 1, name: ‘robin‘}]
const mammals = [{id: 1, name: ‘dog‘}]
// want single array of all animals deduped by id
A simple concat
would improperly duplicate the id 1 animal.
_.unionWith()
lets us merge by rules:
_.unionWith(birds, mammals, (bird, mammal) => {
return bird.id == mammal.id
})
// [{id: 1, name: ‘robin‘}]
The custom comparator function ensures no dupes by id when merging.
Some other handy uses:
- Merging API data from multiple endpoints based on domain rules
- Combine notifications from multiple channels while preventing duplicates
- Merge customer lists from various sources deduped by identifier
Defining merging at the element level allows flexible combination of specialized arrays.
10. intersectionWith – Find Common Elements
The inverse of unionWith
, intersectionWith()
returns only shared elements between arrays according to custom rules.
For example, finding the common IDs between two animal arrays:
const birds = [{id: 1, name: ‘robin‘}]
const mammals = [{id: 1, name: ‘dog‘}, {id: 2, name: ‘cat‘}]
_.intersectionWith(birds, mammals, (bird, mammal) => {
return bird.id === mammal.id
})
// [{id: 1, name: ‘robin‘}]
Even though the objects are different, our custom comparator examines the id to find matches.
Use cases:
- Discover overlapping sets based on identifiers
- Find similar objects across domains with custom equality checks
- Filter joined API responses to common subset
Defining intersection at the element level provides precise control over set logic.
Level Up Your JavaScript with Lodash
I hope these practical examples have showcased how Lodash can help improve our daily JavaScript coding in many ways.
Lodash offers over 100 similar utilities like these that enhance code quality, brevity, and performance.
The library provides a solid set of reusable solutions for working with:
- Collections (arrays, objects)
- Functions (binding, currying)
- Objects (defaults, cloning)
- Strings (templating, manipulation)
- Utilities (random, sorting)
I highly recommend browsing the full Lodash docs to find more handy functions to try.
A quick way to get started is adding Lodash alongside your project‘s current tooling:
npm install lodash
Import the full library or specific methods as needed:
import _ from ‘lodash‘ // full
import {uniq, merge} from ‘lodash‘ // specific
Then apply Lodash utilities in place of manual data transformations.
The goal is cleaner and simpler code that better conveys intent.
Make Lodash a standard part of your JavaScript toolkit, and you‘ll write better code faster. No more reinventing basic utilities – just focus on delivering features!