A small utility library inspired by Lodash
There are some high level goals for this repo;
- To show that you might not need to import Lodash to achieve your goals
- A method for me to learn how some of these functions actually work
- This isn't meant to be a complete replacement for every Lodash function
- Each function is intentionally a smaller, easier-to-read subset rather than a full Lodash clone
- Lodash doesn't treat each function in isolation, this library does for easy copy & pasting
- Lodash re-uses a lot of functions as helpers or uses it's own collection of helpers, obscuring what the function is actually doing
- This should mean that each function in this library should be copy-pasteable
- This is also only achieved if each function is dependency free (even at the cost of duplication of logic)
- To use TypeScript to describe the supported inputs clearly, without pretending each function handles every edge case Lodash does
This library is not trying to become a line-for-line Lodash replacement.
The bar for each function is:
- You should be able to read the whole implementation quickly and understand how it works
- The supported behavior should be obvious from the code, tests, and types
- Correctness bugs in the supported scope should be fixed
- Complexity should only be added when it pays for itself in readability and confidence
That means:
- Prefer a smaller, explicit feature set over broad Lodash-style compatibility
- Prefer honest types over very wide overloads that suggest unsupported behavior
- Prefer tests that document intended behavior over tests that only chase parity
- Avoid deep helper systems that make individual utilities harder to understand
- Be explicit about supported shapes when a utility is intentionally narrow, such as
mergeonly supporting arrays and plain objects
Type parameter names should describe what they represent.
Good examples:
ArrayItemCollectionItemFunctionArgumentsReturnedValue
Avoid single-letter names such as T, R, or P when a descriptive name would make the function easier to read.
While bundling the code for the browser, tree-shaking of libraries such as Lodash happens automatically. However, testing frameworks such as Jest don't do this tree-shaking by design. This means that Jest will load the entire library (such as Lodash) into memory for each test file, using a lot of memory and causing slower tests.
In practise, removing barrel imports halved the run time of tests at my company despite the codebase increasing in size by 40%.
| Function | Version exists? |
|---|---|
| (Array) _.chunk | /src/chunk/chunk.ts |
| (Array) _.compact | /src/compact/compact.ts |
| (Array) _.concat | /src/concat/concat.ts |
| (Array) _.difference | /src/difference/difference.ts |
| (Array) _.differenceBy | /src/differenceBy/differenceBy.ts |
| (Array) _.differenceWith | /src/differenceWith/differenceWith.ts |
| (Array) _.drop | /src/drop/drop.ts |
| (Array) _.dropRight | /src/dropRight/dropRight.ts |
| (Array) _.dropRightWhile | ❌ |
| (Array) _.dropWhile | ❌ |
| (Array) _.fill | ❌ |
| (Array) _.findIndex | /src/findIndex/findIndex.ts |
| (Array) _.findLastIndex | ❌ |
| (Array) _.first -> head | /src/head/head.ts |
| (Array) _.flatten | /src/flatten/flatten.ts |
| (Array) _.flattenDeep | /src/flattenDeep/flattenDeep.ts |
| (Array) _.flattenDepth | /src/flattenDepth/flattenDepth.ts |
| (Array) _.fromPairs | /src/fromPairs/fromPairs.ts |
| (Array) _.head | /src/head/head.ts |
| (Array) _.indexOf | /src/indexOf/indexOf.ts |
| (Array) _.initial | ❌ |
| (Array) _.intersection | /src/intersection/intersection.ts |
| (Array) _.intersectionBy | ❌ |
| (Array) _.intersectionWith | ❌ |
| (Array) _.join | ❌ |
| (Array) _.last | /src/last/last.ts |
| (Array) _.lastIndexOf | ❌ |
| (Array) _.nth | /src/nth/nth.ts |
| (Array) _.pull | ❌ |
| (Array) _.pullAll | ❌ |
| (Array) _.pullAllBy | ❌ |
| (Array) _.pullAllWith | ❌ |
| (Array) _.pullAt | ❌ |
| (Array) _.remove | ❌ |
| (Array) _.reverse | ❌ |
| (Array) _.slice | ❌ |
| (Array) _.sortedIndex | ❌ |
| (Array) _.sortedIndexBy | ❌ |
| (Array) _.sortedIndexOf | ❌ |
| (Array) _.sortedLastIndex | ❌ |
| (Array) _.sortedLastIndexBy | ❌ |
| (Array) _.sortedLastIndexOf | ❌ |
| (Array) _.sortedUniq | ❌ |
| (Array) _.sortedUniqBy | ❌ |
| (Array) _.tail | /src/tail/tail.ts |
| (Array) _.take | /src/take/take.ts |
| (Array) _.takeRight | /src/takeRight/takeRight.ts |
| (Array) _.takeRightWhile | ❌ |
| (Array) _.takeWhile | ❌ |
| (Array) _.union | /src/union/union.ts |
| (Array) _.unionBy | ❌ |
| (Array) _.unionWith | ❌ |
| (Array) _.uniq | /src/uniq/uniq.ts |
| (Array) _.uniqBy | ❌ |
| (Array) _.uniqWith | ❌ |
| (Array) _.unzip | ❌ |
| (Array) _.unzipWith | ❌ |
| (Array) _.without | /src/without/without.ts |
| (Array) _.xor | ❌ |
| (Array) _.xorBy | ❌ |
| (Array) _.xorWith | ❌ |
| (Array) _.zip | ❌ |
| (Array) _.zipObject | ❌ |
| (Array) _.zipObjectDeep | ❌ |
| (Array) _.zipWith | ❌ |
| (Collection) _.countBy | ❌ |
| (Collection) _.each -> forEach | ❌ |
| (Collection) _.eachRight -> forEachRight | ❌ |
| (Collection) _.every | /src/every/every.ts |
| (Collection) _.filter | /src/filter/filter.ts |
| (Collection) _.find | /src/find/find.ts |
| (Collection) _.findLast | ❌ |
| (Collection) _.flatMap | ❌ |
| (Collection) _.flatMapDeep | ❌ |
| (Collection) _.flatMapDepth | ❌ |
| (Collection) _.forEach | ❌ |
| (Collection) _.forEachRight | ❌ |
| (Collection) _.groupBy | /src/groupBy/groupBy.ts |
| (Collection) _.includes | /src/includes/includes.ts |
| (Collection) _.invokeMap | ❌ |
| (Collection) _.keyBy | ❌ |
| (Collection) _.map | /src/map/map.ts |
| (Collection) _.orderBy | ❌ |
| (Collection) _.partition | ❌ |
| (Collection) _.reduce | ❌ |
| (Collection) _.reduceRight | ❌ |
| (Collection) _.reject | ❌ |
| (Collection) _.sample | ❌ |
| (Collection) _.sampleSize | ❌ |
| (Collection) _.shuffle | ❌ |
| (Collection) _.size | /src/size/size.ts |
| (Collection) _.some | /src/some/some.ts |
| (Collection) _.sortBy | /src/sortBy/sortBy.ts |
| (Date) _.now | ❌ |
| (Function) _.after | ❌ |
| (Function) _.ary | ❌ |
| (Function) _.before | ❌ |
| (Function) _.bind | ❌ |
| (Function) _.bindKey | ❌ |
| (Function) _.curry | ❌ |
| (Function) _.curryRight | ❌ |
| (Function) _.debounce | ❌ |
| (Function) _.defer | ❌ |
| (Function) _.delay | ❌ |
| (Function) _.flip | ❌ |
| (Function) _.memoize | /src/memoize/memoize.ts |
| (Function) _.negate | ❌ |
| (Function) _.once | /src/once/once.ts |
| (Function) _.overArgs | ❌ |
| (Function) _.partial | ❌ |
| (Function) _.partialRight | ❌ |
| (Function) _.rearg | ❌ |
| (Function) _.rest | ❌ |
| (Function) _.spread | ❌ |
| (Function) _.throttle | /src/throttle/throttle.ts |
| (Function) _.unary | ❌ |
| (Function) _.wrap | ❌ |
| (Lang) _.castArray | /src/castArray/castArray.ts |
| (Lang) _.clone | ❌ |
| (Lang) _.cloneDeep | /src/cloneDeep/cloneDeep.ts |
| (Lang) _.cloneDeepWith | ❌ |
| (Lang) _.cloneWith | ❌ |
| (Lang) _.conformsTo | ❌ |
| (Lang) _.eq | ❌ |
| (Lang) _.gt | ❌ |
| (Lang) _.gte | ❌ |
| (Lang) _.isArguments | ❌ |
| (Lang) _.isArray | ❌ |
| (Lang) _.isArrayBuffer | ❌ |
| (Lang) _.isArrayLike | ❌ |
| (Lang) _.isArrayLikeObject | ❌ |
| (Lang) _.isBoolean | ❌ |
| (Lang) _.isBuffer | ❌ |
| (Lang) _.isDate | ❌ |
| (Lang) _.isElement | ❌ |
| (Lang) _.isEmpty | /src/isEmpty/isEmpty.ts |
| (Lang) _.isEqual | /src/isEqual/isEqual.ts |
| (Lang) _.isEqualWith | ❌ |
| (Lang) _.isError | ❌ |
| (Lang) _.isFinite | ❌ |
| (Lang) _.isFunction | ❌ |
| (Lang) _.isInteger | ❌ |
| (Lang) _.isLength | ❌ |
| (Lang) _.isMap | ❌ |
| (Lang) _.isMatch | ❌ |
| (Lang) _.isMatchWith | ❌ |
| (Lang) _.isNaN | ❌ |
| (Lang) _.isNative | ❌ |
| (Lang) _.isNil | ❌ |
| (Lang) _.isNull | ❌ |
| (Lang) _.isNumber | ❌ |
| (Lang) _.isObject | ❌ |
| (Lang) _.isObjectLike | ❌ |
| (Lang) _.isPlainObject | ❌ |
| (Lang) _.isRegExp | ❌ |
| (Lang) _.isSafeInteger | ❌ |
| (Lang) _.isSet | ❌ |
| (Lang) _.isString | ❌ |
| (Lang) _.isSymbol | ❌ |
| (Lang) _.isTypedArray | ❌ |
| (Lang) _.isUndefined | ❌ |
| (Lang) _.isWeakMap | ❌ |
| (Lang) _.isWeakSet | ❌ |
| (Lang) _.lt | ❌ |
| (Lang) _.lte | ❌ |
| (Lang) _.toArray | ❌ |
| (Lang) _.toFinite | ❌ |
| (Lang) _.toInteger | ❌ |
| (Lang) _.toLength | ❌ |
| (Lang) _.toNumber | ❌ |
| (Lang) _.toPlainObject | ❌ |
| (Lang) _.toSafeInteger | ❌ |
| (Lang) _.toString | ❌ |
| (Math) _.add | ❌ |
| (Math) _.ceil | ❌ |
| (Math) _.divide | ❌ |
| (Math) _.floor | ❌ |
| (Math) _.max | ❌ |
| (Math) _.maxBy | ❌ |
| (Math) _.mean | ❌ |
| (Math) _.meanBy | ❌ |
| (Math) _.min | ❌ |
| (Math) _.minBy | ❌ |
| (Math) _.multiply | ❌ |
| (Math) _.round | ❌ |
| (Math) _.subtract | ❌ |
| (Math) _.sum | ❌ |
| (Math) _.sumBy | ❌ |
| (Number) _.clamp | ❌ |
| (Number) _.inRange | ❌ |
| (Number) _.random | ❌ |
| (Object) _.assign | ❌ |
| (Object) _.assignIn | ❌ |
| (Object) _.assignInWith | ❌ |
| (Object) _.assignWith | ❌ |
| (Object) _.at | ❌ |
| (Object) _.create | ❌ |
| (Object) _.defaults | ❌ |
| (Object) _.defaultsDeep | ❌ |
| (Object) _.entries -> toPairs | ❌ |
| (Object) _.entriesIn -> toPairsIn | ❌ |
| (Object) _.extend -> assignIn | ❌ |
| (Object) _.extendWith -> assignInWith | ❌ |
| (Object) _.findKey | ❌ |
| (Object) _.findLastKey | ❌ |
| (Object) _.forIn | ❌ |
| (Object) _.forInRight | ❌ |
| (Object) _.forOwn | ❌ |
| (Object) _.forOwnRight | ❌ |
| (Object) _.functions | ❌ |
| (Object) _.functionsIn | ❌ |
| (Object) _.get | ❌ |
| (Object) _.has | /src/has/has.ts |
| (Object) _.hasIn | ❌ |
| (Object) _.invert | ❌ |
| (Object) _.invertBy | ❌ |
| (Object) _.invoke | ❌ |
| (Object) _.keys | /src/keys/keys.ts |
| (Object) _.keysIn | ❌ |
| (Object) _.mapKeys | ❌ |
| (Object) _.mapValues | ❌ |
| (Object) _.merge | /src/merge/merge.ts |
| (Object) _.mergeWith | ❌ |
| (Object) _.omit | ❌ |
| (Object) _.omitBy | ❌ |
| (Object) _.pick | /src/pick/pick.ts |
| (Object) _.pickBy | ❌ |
| (Object) _.result | ❌ |
| (Object) _.set | ❌ |
| (Object) _.setWith | ❌ |
| (Object) _.toPairs | /src/toPairs/toPairs.ts |
| (Object) _.toPairsIn | ❌ |
| (Object) _.transform | ❌ |
| (Object) _.unset | ❌ |
| (Object) _.update | ❌ |
| (Object) _.updateWith | ❌ |
| (Object) _.values | /src/values/values.ts |
| (Object) _.valuesIn | ❌ |
| (String) _.camelCase | /src/camelCase/camelCase.ts |
| (String) _.capitalize | /src/capitalize/capitalize.ts |
| (String) _.deburr | /src/deburr/deburr.ts |
| (String) _.endsWith | /src/endsWith/endsWith.ts |
| (String) _.escape | /src/escape/escape.ts |
| (String) _.escapeRegExp | /src/escapeRegExp/escapeRegExp.ts |
| (String) _.kebabCase | /src/kebabCase/kebabCase.ts |
| (String) _.lowerCase | /src/lowerCase/lowerCase.ts |
| (String) _.lowerFirst | /src/lowerFirst/lowerFirst.ts |
| (String) _.pad | /src/pad/pad.ts |
| (String) _.padEnd | /src/padEnd/padEnd.ts |
| (String) _.padStart | /src/padStart/padStart.ts |
| (String) _.parseInt | /src/parseInt/parseInt.ts |
| (String) _.repeat | /src/repeat/repeat.ts |
| (String) _.replace | /src/replace/replace.ts |
| (String) _.snakeCase | /src/snakeCase/snakeCase.ts |
| (String) _.split | /src/split/split.ts |
| (String) _.startCase | /src/startCase/startCase.ts |
| (String) _.startsWith | /src/startsWith/startsWith.ts |
| (String) _.template | ❌ |
| (String) _.toLower | /src/toLower/toLower.ts |
| (String) _.toUpper | /src/toUpper/toUpper.ts |
| (String) _.trim | /src/trim/trim.ts |
| (String) _.trimEnd | /src/trimEnd/trimEnd.ts |
| (String) _.trimStart | /src/trimStart/trimStart.ts |
| (String) _.truncate | /src/truncate/truncate.ts |
| (String) _.unescape | /src/unescape/unescape.ts |
| (String) _.upperCase | /src/upperCase/upperCase.ts |
| (String) _.upperFirst | /src/upperFirst/upperFirst.ts |
| (String) _.words | /src/words/words.ts |
| (Util) _.attempt | ❌ |
| (Util) _.bindAll | ❌ |
| (Util) _.cond | ❌ |
| (Util) _.conforms | ❌ |
| (Util) _.constant | /src/constant/constant.ts |
| (Util) _.defaultTo | /src/defaultTo/defaultTo.ts |
| (Util) _.flow | ❌ |
| (Util) _.flowRight | ❌ |
| (Util) _.identity | /src/identity/identity.ts |
| (Util) _.iteratee | ❌ |
| (Util) _.matches | ❌ |
| (Util) _.matchesProperty | ❌ |
| (Util) _.method | ❌ |
| (Util) _.methodOf | ❌ |
| (Util) _.mixin | ❌ |
| (Util) _.noConflict | ❌ |
| (Util) _.noop | /src/noop/noop.ts |
| (Util) _.nthArg | ❌ |
| (Util) _.over | ❌ |
| (Util) _.overEvery | ❌ |
| (Util) _.overSome | ❌ |
| (Util) _.property | ❌ |
| (Util) _.propertyOf | ❌ |
| (Util) _.range | /src/range/range.ts |
| (Util) _.rangeRight | ❌ |
| (Util) _.runInContext | ❌ |
| (Util) _.stubArray | ❌ |
| (Util) _.stubFalse | ❌ |
| (Util) _.stubObject | ❌ |
| (Util) _.stubString | ❌ |
| (Util) _.stubTrue | ❌ |
| (Util) _.times | ❌ |
| (Util) _.toPath | ❌ |
| (Util) _.uniqueId | ❌ |