Lodash Complete Guide | JavaScript Utility Library for Arrays, Objects, and More
이 글의 핵심
Lodash is the most popular JavaScript utility library. It provides functions for arrays, objects, strings, functions, and more — making JavaScript development easier and more consistent.
Introduction
Lodash is a modern JavaScript utility library delivering modularity, performance, and extras. It makes working with arrays, objects, strings, functions, and numbers easier and more consistent.
Why Lodash?
Native JavaScript:
// Deep clone (doesn't work properly)
const clone = JSON.parse(JSON.stringify(obj)); // Loses functions, dates
// Debounce (complex to implement)
let timeout;
function debounce(fn, delay) {
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => fn(...args), delay);
};
}
With Lodash:
// Deep clone
const clone = _.cloneDeep(obj);
// Debounce
const debouncedFn = _.debounce(fn, delay);
1. Installation
# npm
npm install lodash
# yarn
yarn add lodash
# CDN (browser)
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
Import Methods
// Import entire library
import _ from 'lodash';
// Import individual functions (recommended for tree-shaking)
import debounce from 'lodash/debounce';
import cloneDeep from 'lodash/cloneDeep';
// CommonJS
const _ = require('lodash');
const debounce = require('lodash/debounce');
2. Array Methods
Map, Filter, Reduce
const users = [
{ id: 1, name: 'Alice', age: 30, active: true },
{ id: 2, name: 'Bob', age: 25, active: false },
{ id: 3, name: 'Charlie', age: 35, active: true },
];
// Map
_.map(users, 'name'); // ['Alice', 'Bob', 'Charlie']
_.map(users, user => user.age * 2); // [60, 50, 70]
// Filter
_.filter(users, { active: true }); // Active users
_.filter(users, user => user.age > 30); // Age > 30
// Find
_.find(users, { name: 'Alice' }); // First match
_.findLast(users, { active: true }); // Last match
// Reduce
_.reduce(users, (sum, user) => sum + user.age, 0); // Total age: 90
Unique, Flatten, Chunk
// Unique
_.uniq([1, 2, 2, 3, 3, 4]); // [1, 2, 3, 4]
_.uniqBy([{ x: 1 }, { x: 2 }, { x: 1 }], 'x'); // [{ x: 1 }, { x: 2 }]
// Flatten
_.flatten([1, [2, [3, [4]]]]); // [1, 2, [3, [4]]]
_.flattenDeep([1, [2, [3, [4]]]]); // [1, 2, 3, 4]
// Chunk (split into groups)
_.chunk([1, 2, 3, 4, 5, 6], 2); // [[1, 2], [3, 4], [5, 6]]
_.chunk(['a', 'b', 'c', 'd'], 3); // [['a', 'b', 'c'], ['d']]
// Compact (remove falsy values)
_.compact([0, 1, false, 2, '', 3, null, undefined]); // [1, 2, 3]
Difference, Intersection, Union
const arr1 = [1, 2, 3];
const arr2 = [2, 3, 4];
// Difference (in arr1 but not arr2)
_.difference(arr1, arr2); // [1]
// Intersection (in both)
_.intersection(arr1, arr2); // [2, 3]
// Union (all unique values)
_.union(arr1, arr2); // [1, 2, 3, 4]
// With objects
const users1 = [{ id: 1 }, { id: 2 }];
const users2 = [{ id: 2 }, { id: 3 }];
_.differenceBy(users1, users2, 'id'); // [{ id: 1 }]
_.intersectionBy(users1, users2, 'id'); // [{ id: 2 }]
Sorting
const users = [
{ name: 'Charlie', age: 35 },
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
];
// Sort by single property
_.sortBy(users, 'age');
// [{ name: 'Bob', age: 25 }, { name: 'Alice', age: 30 }, { name: 'Charlie', age: 35 }]
// Sort by multiple properties
_.sortBy(users, ['age', 'name']);
// Sort with custom order
_.orderBy(users, ['age'], ['desc']);
// [{ name: 'Charlie', age: 35 }, ...]
Take, Drop, Sample
const arr = [1, 2, 3, 4, 5];
// Take first n
_.take(arr, 2); // [1, 2]
_.takeRight(arr, 2); // [4, 5]
// Drop first n
_.drop(arr, 2); // [3, 4, 5]
_.dropRight(arr, 2); // [1, 2, 3]
// Random sample
_.sample(arr); // Random element
_.sampleSize(arr, 2); // 2 random elements
// Shuffle
_.shuffle([1, 2, 3, 4, 5]); // Random order
3. Object Methods
Get, Set, Has
const user = {
name: 'Alice',
address: {
city: 'New York',
zip: '10001',
},
};
// Get (safe nested access)
_.get(user, 'address.city'); // 'New York'
_.get(user, 'address.country', 'USA'); // 'USA' (default)
_.get(user, 'profile.bio'); // undefined
// Set (create nested path)
_.set(user, 'profile.bio', 'Developer');
// { name: 'Alice', address: {...}, profile: { bio: 'Developer' } }
// Has
_.has(user, 'address.city'); // true
_.has(user, 'address.country'); // false
Pick, Omit
const user = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
password: 'secret',
role: 'admin',
};
// Pick (select properties)
_.pick(user, ['id', 'name', 'email']);
// { id: 1, name: 'Alice', email: 'alice@example.com' }
// Omit (exclude properties)
_.omit(user, ['password', 'role']);
// { id: 1, name: 'Alice', email: 'alice@example.com' }
Merge, Assign
const obj1 = { a: 1, b: { x: 1 } };
const obj2 = { b: { y: 2 }, c: 3 };
// Merge (deep)
_.merge(obj1, obj2);
// { a: 1, b: { x: 1, y: 2 }, c: 3 }
// Assign (shallow)
_.assign({}, obj1, obj2);
// { a: 1, b: { y: 2 }, c: 3 }
Keys, Values, Entries
const obj = { a: 1, b: 2, c: 3 };
// Keys
_.keys(obj); // ['a', 'b', 'c']
// Values
_.values(obj); // [1, 2, 3]
// Entries (key-value pairs)
_.toPairs(obj); // [['a', 1], ['b', 2], ['c', 3]]
// From entries
_.fromPairs([['a', 1], ['b', 2]]); // { a: 1, b: 2 }
Clone
const obj = {
name: 'Alice',
address: { city: 'NYC' },
hobbies: ['coding', 'reading'],
};
// Shallow clone
const shallow = _.clone(obj);
shallow.address.city = 'LA'; // Affects original!
// Deep clone
const deep = _.cloneDeep(obj);
deep.address.city = 'LA'; // Doesn't affect original
4. Collection Methods
const users = [
{ name: 'Alice', age: 30, active: true },
{ name: 'Bob', age: 25, active: false },
{ name: 'Charlie', age: 35, active: true },
];
// Group by
_.groupBy(users, 'active');
// {
// true: [{ name: 'Alice', ... }, { name: 'Charlie', ... }],
// false: [{ name: 'Bob', ... }]
// }
// Count by
_.countBy(users, 'active'); // { true: 2, false: 1 }
// Key by (index by property)
_.keyBy(users, 'name');
// {
// Alice: { name: 'Alice', ... },
// Bob: { name: 'Bob', ... },
// Charlie: { name: 'Charlie', ... }
// }
// Partition (split by condition)
_.partition(users, { active: true });
// [[active users], [inactive users]]
5. Function Utilities
Debounce
// Debounce (wait until user stops typing)
const search = _.debounce((query) => {
console.log('Searching for:', query);
}, 300);
// User types: a, ab, abc
// Only searches once after 300ms of no typing
// Cancel pending debounce
search.cancel();
// Execute immediately
search.flush();
Throttle
// Throttle (execute at most once per interval)
const handleScroll = _.throttle(() => {
console.log('Scrolling');
}, 100);
window.addEventListener('scroll', handleScroll);
// Execute at most once per 100ms
// Cancel
handleScroll.cancel();
Once
// Execute only once
const initialize = _.once(() => {
console.log('Initialized');
});
initialize(); // Logs: "Initialized"
initialize(); // Does nothing
initialize(); // Does nothing
Memoize
// Cache function results
const fibonacci = _.memoize((n) => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
fibonacci(40); // First call: slow
fibonacci(40); // Cached: instant
// Custom cache key
const getUser = _.memoize(
async (id) => {
const response = await fetch(`/users/${id}`);
return response.json();
},
(id) => `user-${id}` // Cache key
);
Curry
// Curry (partial application)
const add = (a, b, c) => a + b + c;
const curriedAdd = _.curry(add);
curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6
curriedAdd(1)(2, 3); // 6
// Useful for reusable functions
const add5 = curriedAdd(5);
add5(10, 15); // 30
6. String Methods
// Case conversion
_.camelCase('hello world'); // 'helloWorld'
_.snakeCase('helloWorld'); // 'hello_world'
_.kebabCase('helloWorld'); // 'hello-world'
_.startCase('hello world'); // 'Hello World'
// Truncate
_.truncate('This is a very long string', { length: 15 });
// 'This is a ve...'
// Pad
_.pad('hi', 8); // ' hi '
_.padStart('5', 3, '0'); // '005'
_.padEnd('5', 3, '0'); // '500'
// Trim
_.trim(' hello '); // 'hello'
_.trimStart(' hello '); // 'hello '
_.trimEnd(' hello '); // ' hello'
7. Number Methods
// Random
_.random(1, 10); // Random int between 1 and 10
_.random(1.5, 5.5, true); // Random float
// Clamp (constrain to range)
_.clamp(10, 0, 5); // 5
_.clamp(-5, 0, 10); // 0
_.clamp(3, 0, 10); // 3
// In range
_.inRange(3, 2, 4); // true
_.inRange(5, 8); // true (0 to 8)
8. Real-World Examples
API Request with Debounce
import debounce from 'lodash/debounce';
import axios from 'axios';
const searchAPI = async (query) => {
const response = await axios.get('/api/search', {
params: { q: query }
});
return response.data;
};
// Debounce to avoid excessive API calls
const debouncedSearch = debounce(searchAPI, 300);
// React example
function SearchInput() {
const [results, setResults] = useState([]);
const handleChange = async (e) => {
const query = e.target.value;
if (query.length > 2) {
const data = await debouncedSearch(query);
setResults(data);
}
};
return <input onChange={handleChange} />;
}
Data Transformation
import { groupBy, mapValues, sortBy } from 'lodash';
const orders = [
{ id: 1, customer: 'Alice', amount: 100, status: 'completed' },
{ id: 2, customer: 'Bob', amount: 50, status: 'pending' },
{ id: 3, customer: 'Alice', amount: 200, status: 'completed' },
];
// Group by customer and sum amounts
const customerTotals = mapValues(
groupBy(orders, 'customer'),
(orders) => orders.reduce((sum, order) => sum + order.amount, 0)
);
// { Alice: 300, Bob: 50 }
// Get top customers
const topCustomers = sortBy(
Object.entries(customerTotals),
([_, amount]) => -amount
);
// [['Alice', 300], ['Bob', 50]]
Safe Object Access
import get from 'lodash/get';
// API response
const response = {
data: {
user: {
profile: {
email: 'alice@example.com'
}
}
}
};
// Safe access (no errors if path doesn't exist)
const email = get(response, 'data.user.profile.email');
const phone = get(response, 'data.user.profile.phone', 'N/A');
// Instead of:
// const email = response?.data?.user?.profile?.email;
9. TypeScript Support
import { debounce, cloneDeep } from 'lodash';
interface User {
id: number;
name: string;
}
const users: User[] = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
// Type-safe clone
const cloned: User[] = cloneDeep(users);
// Type-safe debounce
const handleSearch = debounce((query: string) => {
console.log(query);
}, 300);
10. Bundle Size Optimization
// Bad: imports entire library (~70KB)
import _ from 'lodash';
_.debounce(fn, 300);
// Good: import only what you need
import debounce from 'lodash/debounce';
debounce(fn, 300);
// Even better: use lodash-es for tree-shaking
import { debounce } from 'lodash-es';
11. Performance Tips
1. Chain Operations
// Good: chain for efficiency
_.chain(users)
.filter({ active: true })
.sortBy('age')
.take(10)
.value();
// Bad: multiple operations
let result = _.filter(users, { active: true });
result = _.sortBy(result, 'age');
result = _.take(result, 10);
2. Reuse Functions
// Good: create once, use many times
const debouncedSearch = _.debounce(search, 300);
// Bad: create new function every time
onClick={() => _.debounce(search, 300)()}
Summary
Lodash simplifies JavaScript development:
- Array/Object manipulation
- Function utilities (debounce, throttle, memoize)
- Deep cloning and safe object access
- String formatting
- Cross-browser consistency
Key Takeaways:
- Import individual functions to reduce bundle size
- Use debounce/throttle for performance
- Deep clone with _.cloneDeep
- Safe nested access with _.get
- Group/transform data with collection methods
Next Steps:
- Compare Ramda
- Learn JavaScript
- Optimize with Webpack
Resources: