lodash vs underscore vs ramda vs radash
Utility Libraries for JavaScript Data Manipulation and Functional Programming
lodashunderscoreramdaradashSimilar Packages:
Utility Libraries for JavaScript Data Manipulation and Functional Programming

lodash, radash, ramda, and underscore are utility libraries that provide helper functions for common programming tasks in JavaScript, especially around data manipulation, functional composition, and array/object handling. While they share overlapping capabilities, each has a distinct philosophy: underscore pioneered the space with simple utility methods; lodash evolved it with performance optimizations and modularity; ramda embraces pure functional programming with automatic currying and immutable patterns; and radash is a modern alternative focused on TypeScript support, readability, and developer ergonomics without legacy baggage.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
lodash84,530,09161,458-1005 years agoMIT
underscore16,399,47727,385906 kB52a year agoMIT
ramda12,448,41124,1021.2 MB1472 months agoMIT
radash632,6794,815306 kB1156 months agoMIT

Utility Libraries Compared: lodash vs radash vs ramda vs underscore

When you're knee-deep in JavaScript data wrangling — filtering arrays, transforming objects, debouncing inputs — reaching for a utility library feels natural. But with lodash, radash, ramda, and underscore all claiming similar ground, how do you pick the right one? Let’s cut through the noise with real code and practical trade-offs.

⚠️ First: Is Underscore Still Viable?

No. As of 2024, underscore is effectively deprecated. Its GitHub README states: "Underscore is no longer under active development." It hasn’t had a meaningful release since 2019, offers no TypeScript definitions out of the box, and lacks modern optimizations like tree-shaking support. If you see it in a legacy codebase, plan a migration — but never start a new project with it.

That leaves us with three serious contenders: lodash, ramda, and the newcomer radash.

🧩 Core Philosophy: How Each Library Thinks About Code

lodash: Practical Performance

lodash prioritizes speed, flexibility, and backward compatibility. It supports both chained and functional styles, mutates only when explicitly asked (e.g., _.pull), and gives you escape hatches for edge cases. It’s the “Swiss Army knife” — not always elegant, but gets the job done.

// lodash: Mix of styles
import _ from 'lodash';

const result = _.chain(users)
  .filter(u => u.active)
  .map(u => ({ ...u, name: u.name.toUpperCase() }))
  .value();

// Or functional
const result2 = _.map(
  _.filter(users, 'active'),
  u => ({ ...u, name: u.name.toUpperCase() })
);

ramda: Pure Functional Discipline

ramda enforces immutability and function composition. Every function is automatically curried, data always comes last, and nothing ever mutates your inputs. This makes code highly composable but can feel rigid if you’re used to imperative styles.

// ramda: Point-free composition
import { filter, map, propEq, toUpper, evolve } from 'ramda';

const processUsers = map(
  evolve({ name: toUpper }),
  filter(propEq('active', true))
);

const result = processUsers(users);

radash: Modern Clarity

radash strips away legacy patterns. No chaining. No global _. Functions take explicit arguments in intuitive order (data first), return new objects/arrays, and come with first-class TypeScript types. It’s built for developers who value readability over cleverness.

// radash: Explicit and typed
import { filter, map } from 'radash';

const activeUsers = filter(users, u => u.active);
const result = map(activeUsers, u => ({ ...u, name: u.name.toUpperCase() }));

🔍 Deep Dive: Common Tasks Compared

Task 1: Debouncing a Function

lodash offers fine-grained control:

import debounce from 'lodash/debounce';

const debouncedSave = debounce(saveDraft, 300, { leading: false, trailing: true });

ramda doesn’t include debouncing — it’s considered a side-effect, outside its pure FP scope. You’d use a separate library or write your own.

radash includes a simple, typed debounce:

import { debounce } from 'radash';

const debouncedSave = debounce(300, saveDraft);

✅ Verdict: Need advanced debounce options? lodash. Want simplicity with types? radash. Avoid ramda here.

Task 2: Deep Cloning an Object

lodash:

import cloneDeep from 'lodash/cloneDeep';

const copy = cloneDepth(original);

ramda has no deep clone — it encourages shallow updates via assocPath or lens. For deep structures, you’re expected to design immutable update paths.

radash:

import { clone } from 'radash';

const copy = clone(original); // Performs deep clone

✅ Verdict: Both lodash and radash handle this. ramda intentionally avoids it.

Task 3: Grouping an Array by a Property

lodash:

import groupBy from 'lodash/groupBy';

const byStatus = groupBy(users, 'status');
// { active: [...], inactive: [...] }

ramda:

import { groupBy, prop } from 'ramda';

const byStatus = groupBy(prop('status'), users);

radash:

import { group } from 'radash';

const byStatus = group(users, user => user.status);

All three work, but note the argument order: radash and lodash put data first; ramda puts data last.

Task 4: Safe Nested Property Access

lodash’s get is legendary:

import get from 'lodash/get';

const city = get(user, 'address.city', 'Unknown');

ramda uses pathOr:

import { pathOr } from 'ramda';

const city = pathOr('Unknown', ['address', 'city'], user);

radash opts for explicit functions:

import { get } from 'radash';

const city = get(user, u => u.address?.city) ?? 'Unknown';

💡 Note: radash’s get uses a selector function, leveraging optional chaining — a modern JS pattern that many prefer over string paths.

📦 Bundle Size & Tree-Shaking Reality

While we won’t cite numbers per instructions, the practical impact matters:

  • lodash: Full import (import _ from 'lodash') bloats bundles. Always use per-method imports (import chunk from 'lodash/chunk').
  • ramda: Tree-shaking works reasonably well, but currying adds slight overhead.
  • radash: Built with ES modules from the ground up — every function is a separate export, making tree-shaking trivial.

🧪 TypeScript Experience

  • lodash: Types are community-maintained via @types/lodash. Generally good, but occasional mismatches.
  • ramda: Excellent official types, though currying can confuse beginners (R.map has multiple overloads).
  • radash: Types are first-class and hand-written. Autocomplete “just works,” and generics infer correctly out of the box.

🛑 When to Avoid Each

  • Avoid lodash if you can’t enforce disciplined imports — accidental full imports kill performance.
  • Avoid ramda if your team isn’t bought into functional programming — its patterns can feel alien in mixed-paradigm codebases.
  • Avoid radash if you need niche utilities (e.g., _.template, _.throttle with leading/trailing flags) — its API is smaller by design.

🔄 Migration Tip: From Lodash to Radash

Many lodash methods have direct radash equivalents:

lodashradash
_.chunk(array, 3)chunk(array, 3)
_.omit(obj, keys)omit(obj, keys)
_.pick(obj, keys)pick(obj, keys)
_.range(5)range(0, 5)

But note differences: radash.range(start, end) requires two arguments; lodash.range(5) assumes start=0.

✅ Final Recommendation

  • Legacy or performance-critical app?lodash (with modular imports)
  • New TypeScript project valuing clarity?radash
  • Team committed to functional purity?ramda
  • Starting something new in 2024? → Skip underscore entirely

The best utility library is the one that disappears into your code — making intentions clear without fighting your team’s workflow. Choose accordingly.

How to Choose: lodash vs underscore vs ramda vs radash
  • lodash:

    Choose lodash if you need a battle-tested, highly optimized utility library with excellent performance, deep ecosystem integration, and support for both functional and imperative styles. It’s ideal for large applications where bundle size can be managed via cherry-picking, and when working with legacy codebases that already depend on it. Avoid full imports in modern apps—use modular imports like import debounce from 'lodash/debounce' to keep bundles lean.

  • underscore:

    Do not choose underscore for new projects. While historically significant, it is effectively deprecated in modern frontend development. The official GitHub repository states it is 'no longer under active development,' and it lacks TypeScript support, tree-shaking compatibility, and performance optimizations found in alternatives. Migrate existing usage to lodash or radash during refactors.

  • ramda:

    Choose ramda if your team embraces pure functional programming principles—immutable data, point-free style, and automatic currying. Its functions are designed to compose seamlessly, making complex data transformations expressive and testable. However, be prepared for a steeper learning curve and potential performance overhead in hot paths due to its strict immutability and currying model.

  • radash:

    Choose radash if you’re building a new TypeScript-first application and want clean, readable, and well-typed utility functions without historical baggage. It avoids mutation by design, uses intuitive naming (e.g., chunk instead of _.chunk), and favors explicit arguments over chaining. It’s a great fit for teams prioritizing type safety, maintainability, and modern JavaScript practices over maximum API breadth.

README for lodash

lodash v4.17.21

The Lodash library exported as Node.js modules.

Installation

Using npm:

$ npm i -g npm
$ npm i --save lodash

In Node.js:

// Load the full build.
var _ = require('lodash');
// Load the core build.
var _ = require('lodash/core');
// Load the FP build for immutable auto-curried iteratee-first data-last methods.
var fp = require('lodash/fp');

// Load method categories.
var array = require('lodash/array');
var object = require('lodash/fp/object');

// Cherry-pick methods for smaller browserify/rollup/webpack bundles.
var at = require('lodash/at');
var curryN = require('lodash/fp/curryN');

See the package source for more details.

Note:
Install n_ for Lodash use in the Node.js < 6 REPL.

Support

Tested in Chrome 74-75, Firefox 66-67, IE 11, Edge 18, Safari 11-12, & Node.js 8-12.
Automated browser & CI test runs are available.