
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
js-ultimate
Advanced tools
Lightweight, powerful utility functions to replace lodash with zero dependencies
A lightweight, zero-dependency TypeScript utility library designed as a modern Lodash replacement.
# npm
npm install js-ultimate
# yarn
yarn add js-ultimate
# pnpm
pnpm add js-ultimate
# bun
bun add js-ultimate
import { chunk, get, uniq, debounce } from 'js-ultimate'
// Array utilities
chunk([1, 2, 3, 4, 5], 2) // => [[1, 2], [3, 4], [5]]
// Object utilities
const obj = { user: { name: 'Alice', age: 30 } }
get(obj, 'user.name') // => 'Alice'
// Collection utilities
uniq([1, 2, 1, 3, 2]) // => [1, 2, 3]
// Function utilities
const save = debounce(() => saveToServer(), 300)
chunk(array, size)Splits an array into chunks of the specified size.
chunk([1, 2, 3, 4, 5], 2) // => [[1, 2], [3, 4], [5]]
chunk([1, 2, 3], 5) // => [[1, 2, 3]]
compact(array)Removes falsy values from an array.
compact([0, 1, false, 2, '', 3, null, undefined, 4, NaN, 5])
// => [1, 2, 3, 4, 5]
difference(array, values)Returns values in array not present in values.
difference([1, 2, 3, 4], [2, 4]) // => [1, 3]
first(array)Gets the first element of an array.
first([1, 2, 3]) // => 1
first([]) // => undefined
flattenDeep(array)Recursively flattens a nested array.
flattenDeep([1, [2, [3, [4, [5]]]]]) // => [1, 2, 3, 4, 5]
intersection(arrays)Returns the intersection of multiple arrays.
intersection([1, 2, 3], [2, 3, 4], [3, 4, 5]) // => [3]
last(array)Gets the last element of an array.
last([1, 2, 3]) // => 3
last([]) // => undefined
range(start, end?, step?)Creates an array of numbers from start to end.
range(4) // => [0, 1, 2, 3]
range(0, 10) // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(0, 10, 2) // => [0, 2, 4, 6, 8]
uniq(array)Removes duplicate values from an array.
uniq([1, 2, 1, 3, 2]) // => [1, 2, 3]
uniqBy(array, iteratee)Removes duplicates based on a key or function.
uniqBy([{ id: 1 }, { id: 2 }, { id: 1 }], 'id')
// => [{ id: 1 }, { id: 2 }]
uniqBy([2.1, 1.2, 2.3], Math.floor)
// => [2.1, 1.2]
filter(collection, predicate)Iterates over elements of collection, returning an array of all elements predicate returns truthy for.
filter([1, 2, 3, 4], n => n % 2 === 0) // => [2, 4]
filter({ a: 1, b: 2, c: 3 }, n => n > 1) // => [2, 3]
find(collection, predicate)Returns the first element predicate returns truthy for.
find([{ id: 1 }, { id: 2 }], { id: 2 }) // => { id: 2 }
find([1, 2, 3], n => n > 1) // => 2
groupBy(collection, iteratee)Creates an object composed of keys generated from the results of running each element through iteratee.
groupBy([{ type: 'a' }, { type: 'b' }, { type: 'a' }], 'type')
// => { a: [{ type: 'a' }, { type: 'a' }], b: [{ type: 'b' }] }
keyBy(collection, iteratee)Creates an object composed of keys generated from the results of running each element through iteratee.
keyBy([{ id: 'a' }, { id: 'b' }], 'id')
// => { a: { id: 'a' }, b: { id: 'b' } }
map(collection, iteratee)Creates an array of values by running each element in collection through iteratee.
map([1, 2, 3], n => n * 2) // => [2, 4, 6]
map({ a: 1, b: 2 }, (n, k) => k) // => ['a', 'b']
reduce(collection, iteratee, accumulator)Reduces collection to a single value.
reduce([1, 2, 3], (sum, n) => sum + n, 0) // => 6
reduce({ a: 1, b: 2 }, (acc, n) => acc + n, 0) // => 3
cloneDeep(value)Recursively clones a value.
const original = { a: 1, b: { c: 2 } }
const cloned = cloneDeep(original)
cloned.b.c = 3
console.log(original.b.c) // => 2 (unchanged)
get(object, path, defaultValue?)Gets the value at path of object.
const obj = { a: { b: { c: 42 } } }
get(obj, 'a.b.c') // => 42
get(obj, 'a.x.y', 'default') // => 'default'
mergeDeep(target, source)Recursively merges source into target.
mergeDeep({ a: { b: 1, c: 2 } }, { a: { b: 3, d: 4 } })
// => { a: { b: 3, c: 2, d: 4 } }
omit(object, paths)Creates an object composed of the own and inherited enumerable property paths of object that are not omitted.
omit({ a: 1, b: 2, c: 3 }, ['b', 'c']) // => { a: 1 }
omitBy(object, predicate)Creates an object composed of the own and inherited enumerable string keyed properties of object that predicate doesn't return truthy for.
omitBy({ a: 1, b: 2, c: 3 }, v => v === 2) // => { a: 1, c: 3 }
pick(object, paths)Creates an object composed of the picked object properties.
pick({ a: 1, b: 2, c: 3 }, ['a', 'c']) // => { a: 1, c: 3 }
pickBy(object, predicate)Creates an object composed of the own and inherited enumerable string keyed properties of object that predicate returns truthy for.
pickBy({ a: 1, b: 2, c: 3 }, v => v > 1) // => { b: 2, c: 3 }
set(object, path, value)Sets the value at path of object.
const obj = {}
set(obj, 'a.b.c', 42)
// => { a: { b: { c: 42 } } }
Security Note:
set()includes built-in protection against prototype pollution by rejecting paths containing__proto__,constructor, orprototype.
setImmutable(object, path, value)Returns a new object with the value set at path, without mutating the original.
const original = { a: { b: { c: 1 } }, d: 2 }
const updated = setImmutable(original, 'a.b.c', 99)
// original => { a: { b: { c: 1 } }, d: 2 }
// updated => { a: { b: { c: 99 } }, d: 2 }
camelCase(string)Converts string to camel case.
camelCase('foo Bar') // => 'fooBar'
camelCase('--foo-bar--') // => 'fooBar'
camelCase('__FOO_BAR__') // => 'fooBar'
capitalize(string)Capitalizes the first character of string.
capitalize('fred') // => 'Fred'
capitalize('FRED') // => 'FRED'
kebabCase(string)Converts string to kebab case.
kebabCase('foo Bar') // => 'foo-bar'
kebabCase('fooBar') // => 'foo-bar'
kebabCase('__FOO_BAR__') // => 'foo-bar'
snakeCase(string)Converts string to snake case.
snakeCase('foo Bar') // => 'foo_bar'
snakeCase('fooBar') // => 'foo_bar'
snakeCase('--foo-bar--') // => 'foo_bar'
startCase(string)Converts string to start case.
startCase('--foo-bar--') // => 'Foo Bar'
startCase('fooBar') // => 'Foo Bar'
truncate(string, options?)Truncates string if it's longer than the given maximum length.
truncate('hi-diddly-ho there, neighborino')
// => 'hi-diddly-ho there, neighbo...'
truncate('hi-diddly-ho there, neighborino', { length: 24, separator: ' ' })
// => 'hi-diddly-ho there,...'
upperFirst(string)Capitalizes the first character of string.
upperFirst('fred') // => 'Fred'
upperFirst('FRED') // => 'FRED'
debounce(func, wait)Creates a debounced function that delays invoking func until after wait milliseconds have elapsed.
const save = debounce(() => saveToServer(), 300)
// save() will only invoke saveToServer 300ms after the last call
once(func)Creates a function that is restricted to invoking func once.
const initialize = once(() => setupDatabase())
initialize() // runs setupDatabase
initialize() // does nothing
throttle(func, wait)Creates a throttled function that only invokes func at most once per every wait milliseconds.
const scrollHandler = throttle(() => updatePosition(), 100)
window.addEventListener('scroll', scrollHandler)
clamp(number, lower, upper)Clamps number within the inclusive lower and upper bounds.
clamp(-10, -5, 5) // => -5
clamp(10, -5, 5) // => 5
clamp(0, -5, 5) // => 0
isEmpty(value)Checks if value is an empty object, collection, map, or set.
isEmpty([]) // => true
isEmpty({}) // => true
isEmpty('') // => true
isEmpty(null) // => true
isEmpty([1, 2, 3]) // => false
isEmpty({ a: 1 }) // => false
isEqual(value, other)Performs a deep comparison between two values.
isEqual({ a: 1 }, { a: 1 }) // => true
isEqual([1, 2], [1, 2]) // => true
isEqual({ a: { b: 2 } }, { a: { b: 2 } }) // => true
isPlainObject(value)Checks if value is a plain object (created by object literals or Object.create(null)).
isPlainObject({}) // => true
isPlainObject(Object.create(null)) // => true
isPlainObject(new Date()) // => false
isPlainObject([1, 2, 3]) // => false
js-ultimate is optimized for performance. Here's how it compares to Lodash in key operations:
| Operation | js-ultimate | Lodash | Improvement |
|---|---|---|---|
| chunk(1000, 10) | ~2.5M ops/sec | ~1.8M ops/sec | ~39% faster |
| get(nested path) | ~8M ops/sec | ~4.5M ops/sec | ~78% faster |
| set(nested path) | ~3.2M ops/sec | ~1.5M ops/sec | ~113% faster |
| isEqual(nested) | ~1.2M ops/sec | ~0.8M ops/sec | ~50% faster |
| cloneDeep(nested) | ~800K ops/sec | ~500K ops/sec | ~60% faster |
| groupBy(1000) | ~400K ops/sec | ~350K ops/sec | ~14% faster |
| uniq(array) | ~3M ops/sec | ~2.5M ops/sec | ~20% faster |
Benchmarks run with tinybench on Node.js v20. Results may vary based on runtime and environment.
js-ultimate is fully tree-shakable. Import only what you need and your bundler will eliminate the rest.
// Bundle only includes chunk and uniq
import { chunk, uniq } from 'js-ultimate'
// Same as above — tree-shaking works with named exports
import { chunk } from 'js-ultimate/dist/array/chunk.js'
import { uniq } from 'js-ultimate/dist/array/uniq.js'
// package.json
{
"sideEffects": false
}
Built with TypeScript from the ground up. Full type definitions included.
import { get, type GetType, type Paths } from 'js-ultimate'
interface User {
profile: {
name: string
email: string
}
}
const user: User = { profile: { name: 'Alice', email: 'alice@example.com' } }
// Type-safe paths
const name = get(user, 'profile.name') // Type is string
// Utility types for working with paths
type ProfileKeys = Paths<User> // 'profile' | 'profile.name' | 'profile.email'
type NameType = GetType<User, 'profile.name'> // string
| Variant | Size (gzipped) |
|---|---|
| Full build (all 40+ functions) | ~3.5 KB |
| Single function import | ~150-300 B each |
Importing individual functions results in minimal bundle impact — typically 150-300 bytes per function after gzip.
Contributions are welcome! Please follow these guidelines:
yarn testyarn format# Install dependencies
yarn install
# Run tests in watch mode
yarn test
# Run tests once
yarn test --run
# Run benchmarks
yarn bench
# Build the project
yarn build
# Format code
yarn format
MIT © hoatepdev
Made with ❤️ for modern JavaScript applications
FAQs
Lightweight, powerful utility functions to replace lodash with zero dependencies
We found that js-ultimate demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.