10 Essential Lodash Functions to Improve Your JavaScript Coding

default image

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, 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‘ }  
], ‘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:


// [
//   [‘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 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 ==

// [{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 ===  

// [{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!

Written by