Coming from Lodash
Each heading is a Lodash function. The Perkakas equivalent is checked against @vinicunca/perkakas v1.17.1. Names that Remeda documents but Perkakas does not ship are called out. In-page links such as #reverse jump to that Lodash section.
add
Lodash add → add.
assign
Lodash assign → merge.
- In Lodash the merge is done in-place, on the first argument to the function. In Perkakas a new object is always returned, and none of the input objects are mutated.
- In Lodash
assigncan be used to merge any number of objects by accepting a variadic argument list. In Perkakas, if you are merging only 2 objects usemerge, but if you need to merge more than 2 objects usemergeAllinstead, which takes an Array of objects.
// Lodash
const DATA = { a: 1, b: 2 };
assign(DATA, b);
assign(DATA, b, c, d, e);
// Perkakas
let DATA = { a: 1, b: 2 };
DATA = merge(DATA, b);
DATA = mergeAll([DATA, b, c, d, e]);
camelCase
Lodash camelCase → toCamelCase.
- Lodash attempts pseudo-linguistic word splitting to handle special characters,
which might lead to inaccurate results. Perkakas uses a simpler word splitting
approach based on
type-fest's definition that should only be used for simple strings like identifiers and internal keys. For linguistic processing where language and locale nuances matter, use the built-inIntl.Segmenterinstead. - Perkakas treats consecutive uppercase characters differently from Lodash. Use
{ preserveConsecutiveUppercase: false }as the second parameter to get the same results. - Lodash performs normalization on the input before splitting it, including
deburrand removing apostrophes. Perkakas's word splitting is simpler and doesn't include these normalizations, so they need to be done manually if required. - Lodash allows calling
camelCasewithout any input, or with anundefinedinput, which isn't supported in Perkakas. Handle these cases before calling the function.
Simple strings
// Lodash
_.camelCase(input);
// Perkakas
toCamelCase(input, { preserveConsecutiveUppercase: false });
Normalized
// Lodash
_.camelCase(input);
// Perkakas + Native
toCamelCase(
input
// "Promote" diacritics to be independent characters in the string.
.normalize('NFD')
// Remove apostrophes and all independent diacritic characters.
.replace(/['\u2019\u0300-\u036F]/g, ''),
{ preserveConsecutiveUppercase: false },
);
Missing input
// Lodash
_.camelCase();
_.camelCase(input);
// Perkakas
('');
input !== undefined
? toCamelCase(input, { preserveConsecutiveUppercase: false })
: '';
// Or
toCamelCase(input ?? '', { preserveConsecutiveUppercase: false });
capitalize
Lodash capitalize → capitalize.
- Lodash converts the entire string to lowercase first, while Perkakas only
capitalizes the first character. To match Lodash behavior, call
toLowerCasefirst. - For display purposes, prefer using CSS; use
text-transform: lowercaseon the element, then use::first-letterto target just the first letter of the word, and usetext-transform: uppercaseto capitalize it.
Runtime
// Lodash
_.capitalize(input);
// Perkakas
capitalize(toLowerCase(input));
CSS
// Lodash
<div>{_.capitalize(input)}</div>
// CSS
<style>
.capitalize {
text-transform: lowercase;
}
.capitalize::first-letter {
text-transform: uppercase;
}
</style>
<div class="capitalize">{input}</div>
ceil
Lodash ceil → ceil.
chunk
Lodash chunk → chunk.
clamp
Lodash clamp → clamp.
cloneDeep
Lodash cloneDeep → clone.
compact
Lodash compact → filter.
Use isTruthy as the predicate.
// Lodash
compact(DATA);
// Perkakas
filter(DATA, isTruthy);
concat
Lodash concat → concat.
cond
Lodash cond → conditional.
constant
Lodash constant → constant.
countBy
Lodash countBy → countBy.
debounce
Lodash debounce → funnel.
debouncecan be implemented using thefunnelutility. A reference implementation is provided below, and a more expanded version with inline documentation and tests is available in the test filefunnel.lodash-debounce.test.ts.- A more complete reference implementation that also maintains Lodash's
capability to store the callback's return value is available below, and in
funnel.lodash-debounce-with-cached-value.test.ts. - These implementations can be copied as-is into your project, but might contain
redundant parts which are not relevant for your specific use cases. By
inlining only the parts you need you can take advantage of capabilities not
available in Lodash. To copy the expanded versions from the test files
instead, take everything between the
REFERENCE STARTandREFERENCE ENDmarkers.
Reference
import { funnel } from '@vinicunca/perkakas';
function debounce<F extends (...args: any) => void>(
func: F,
wait = 0,
{
leading = false,
trailing = true,
maxWait,
}: {
readonly leading?: boolean;
readonly trailing?: boolean;
readonly maxWait?: number;
} = {},
) {
const {
call,
isIdle: _isIdle,
...rest
} = funnel(
() => {
if (leading || trailing) {
func();
}
},
{
minQuietPeriodMs: wait,
...(maxWait !== undefined && { maxBurstDurationMs: maxWait }),
...(trailing
? leading
? { triggerAt: 'both' }
: { triggerAt: 'end' }
: { triggerAt: 'start' }),
},
);
return Object.assign(call, rest);
}
With call arguments
import { funnel } from '@vinicunca/perkakas';
function debounce<F extends (...args: any) => void>(
func: F,
wait = 0,
{
leading = false,
trailing = true,
maxWait,
}: {
readonly leading?: boolean;
readonly trailing?: boolean;
readonly maxWait?: number;
} = {},
) {
const {
call,
isIdle: _isIdle,
...rest
} = funnel(
(args: Parameters<F>) => {
if (leading || trailing) {
func(...args);
}
},
{
reducer: (_, ...args: Parameters<F>) => args,
minQuietPeriodMs: wait,
...(maxWait !== undefined && { maxBurstDurationMs: maxWait }),
...(trailing
? leading
? { triggerAt: 'both' }
: { triggerAt: 'end' }
: { triggerAt: 'start' }),
},
);
return Object.assign(call, rest);
}
With cached value
import { funnel } from '@vinicunca/perkakas';
function debounce<F extends (...args: any) => any>(
func: F,
wait = 0,
{
leading = false,
trailing = true,
maxWait,
}: {
readonly leading?: boolean;
readonly trailing?: boolean;
readonly maxWait?: number;
} = {},
) {
let cachedValue: ReturnType<F> | undefined;
const { call, flush, cancel } = funnel(
(args: Parameters<F>) => {
if (leading || trailing) {
cachedValue = func(...args) as ReturnType<F>;
}
},
{
reducer: (_, ...args: Parameters<F>) => args,
minQuietPeriodMs: wait,
...(maxWait !== undefined && { maxBurstDurationMs: maxWait }),
...(trailing
? leading
? { triggerAt: 'both' }
: { triggerAt: 'end' }
: { triggerAt: 'start' }),
},
);
return Object.assign(
(...args: Parameters<F>) => {
call(...args);
return cachedValue;
},
{
flush: () => {
flush();
return cachedValue;
},
cancel,
},
);
}
deburr
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
deburr uses hard-coded heuristics which can lead to inaccurate results:
- Misses some accented characters (e.g., ḩ, ố, ῷ and й).
- Erroneously "simplifies" ligatures that don't formally have any diacritics that require "deburring" (e.g., Æ, IJ, Œ, and ʼn).
- Swaps characters because they look similar although they are semantically unrelated to each other (e.g., Ð, Ø, Þ, ß, ſ).
We strongly recommend using linguistically-aware alternatives instead that are purpose-built for this task:
- When comparing strings, prefer
Intl.Collatorwithsensitivity: 'base'to compare them while ignoring accents and case. - When simplifying strings, prefer
normalize("NFD")to decompose characters, then usereplaceto remove diacritics. This solution is more accurate, but is not a drop-in replacement fordeburrbecause of the differences mentioned above. - When querying for a string, prefer purpose built collations and other built-in mechanisms of databases and other systems that offer more robust query normalization.
Comparison
// Lodash
_.deburr('café') === 'cafe';
// Native JS
const collator = new Intl.Collator('en', { sensitivity: 'base' });
collator.compare('café', 'cafe') === 0;
Simplifying
// Lodash
_.deburr(input);
// Native JS
input
.normalize('NFD')
.replace(
/[\u0300-\u036F]/g /* The "Combining Diacritical Marks" Unicode block. */,
'',
);
defaultTo
Lodash defaultTo → defaultTo.
Number.NaNis not considered nullish and would not result in returning the default value. Usewhenwith the built-inNumber.isNaNand aconstantfor the fallback value.- Unlike in Perkakas, Lodash allows the fallback value to be of any type, even
those that are incompatible with the data type. It also allows the data type
to be non-nullish; for those cases use the built-in Nullish coalescing operator
??directly, or usewhenwithisNullishandconstant.
Nullish
const DATA: number | undefined | null;
// Lodash
_.defaultTo(DATA, 456);
// Perkakas
defaultTo(DATA, 456);
NaN
const DATA = Number.NaN;
// Lodash
_.defaultTo(DATA, 10);
// Perkakas
when(DATA, Number.isNaN, constant(10));
Both
const DATA: number | null | undefined;
// Lodash
_.defaultTo(DATA, 10);
// Perkakas
when(
DATA,
(x) => x === undefined || x === null || Number.isNaN(x),
constant(10),
);
Non-matching fallback
const DATA: string | null | undefined;
// Lodash
_.defaultTo(DATA, 123);
// Perkakas
when(DATA, isNullish, constant(123));
// or natively
DATA ?? 123;
difference
Lodash difference → difference.
- Perkakas's
differencetreats the inputs as multisets/bags which respect item duplication, whereas in Lodash all matching values are filtered out. This means Perkakas removes only one copy of each matching item, while Lodash removes all copies. - When the input arrays might contain duplicates and you want to filter all
values use a composition of
filter,isNot, andisIncludedInto replicate the Lodash semantics. - Lodash's
differenceaccepts multiple values arrays as separate arguments and flattens them, while Perkakas'sdifferencetakes a single values array. When migrating these arrays need to be flattened or spread into a single array manually. - Lodash supports calling
differencetrivially, with no exclusion array at all which results in a shallow clone of the input array. In Perkakas the exclusion array is required. - Lodash accepts
nullandundefinedvalues for the array (and treats them as an empty array). In Perkakas this nullish value needs to be handled explicitly either by skipping the call todifference, or by coalescing the input to an empty array.
Without duplicate values (unique)
// Lodash
_.difference(uniqueData, uniqueValues);
// Perkakas
difference(uniqueData, uniqueValues);
With duplicate values
// Lodash
_.difference(dataWithDuplicates, uniqueValues);
_.difference(uniqueData, valuesWithDuplicates);
_.difference(dataWithDuplicates, valuesWithDuplicates);
// Perkakas
filter(dataWithDuplicates, isNot(isIncludedIn(uniqueValues)));
filter(uniqueData, isNot(isIncludedIn(valuesWithDuplicates)));
filter(dataWithDuplicates, isNot(isIncludedIn(valuesWithDuplicates)));
// valuesWithDuplicates doesn't need to be deduped when used inside
// `isIncludedIn`, but can be for efficiency if needed via `unique`
filter(dataWithDuplicates, isNot(isIncludedIn(unique(valuesWithDuplicates))));
Multiple exclusion arrays
// Lodash
_.difference(DATA, a, b, c);
// Perkakas
difference(DATA, [...a, ...b, ...c]);
Missing 2nd (exclusions) parameter
// Lodash
_.difference(DATA);
// Perkakas
difference(DATA, []);
// Or directly via Native JS:
[...DATA];
Nullish inputs
// Lodash
_.difference(DATA, values);
// Perkakas
isNonNullish(DATA) ? difference(DATA, values) : [];
// Or
difference(DATA ?? [], values);
differenceBy
Lodash differenceBy → filter.
Not provided by Perkakas.
- Use a composition based on
filter,isNotisIncludedIn, andmapto recreate the logic fordifferenceBy. - Additionally, when the iteratee parameter is defined as a property name, use
propas the iteratee function instead. - Lodash accepts
nullandundefinedvalues for the array (and treats them as an empty array). In Perkakas this nullish value needs to be handled explicitly either by skipping the call todifferenceBy, or by coalescing the input to an empty array. - When the
iterateeparameter is not provided to the LodashdifferenceByfunction (or is provided asundefined) it behaves like a call todifference.
Function iteratee
// Lodash
_.differenceBy(DATA, values, iteratee);
// Perkakas
filter(DATA, piped(iteratee, isNot(isIncludedIn(map(values, iteratee)))));
// the mapped values don't need to be deduped when used inside `isIncludedIn`,
// but could be for efficiency if needed via `unique`
filter(
DATA,
piped(iteratee, isNot(isIncludedIn(unique(map(values, iteratee))))),
);
Property iteratee
// Lodash
_.differenceBy(DATA, values, 'x');
// Perkakas
filter(DATA, piped(prop('x'), isNot(isIncludedIn(map(values, prop('x'))))));
// the mapped values don't need to be deduped when used inside `isIncludedIn`,
// but could be for efficiency if needed via `unique`
filter(
DATA,
piped(prop('x'), isNot(isIncludedIn(unique(map(values, prop('x')))))),
);
Multiple exclusion arrays
// Lodash
_.differenceBy(DATA, a, b, c, iteratee);
// Perkakas
filter(
DATA,
piped(iteratee, isNot(isIncludedIn(map([...a, ...b, ...c], iteratee)))),
);
// the mapped values don't need to be deduped when used inside `isIncludedIn`,
// but could be for efficiency if needed via `unique`
filter(
DATA,
piped(
iteratee,
isNot(isIncludedIn(unique(map([...a, ...b, ...c], iteratee)))),
),
);
Nullish inputs
// Lodash
_.differenceBy(DATA, values, iteratee);
// Perkakas
isNonNullish(DATA)
? filter(DATA, piped(iteratee, isNot(isIncludedIn(map(values, iteratee)))))
: [];
// Or
filter(DATA ?? [], piped(iteratee, isNot(isIncludedIn(map(values, iteratee)))));
Missing iteratee function
// Lodash
_.differenceBy(DATA, values);
// Convert to Lodash's `difference` and then refer to the `difference` migration
// docs
_.difference(DATA, values);
differenceWith
Lodash differenceWith → differenceWith.
- Lodash's
differenceWithaccepts a variadic list of values arrays which are then effectively flattened, but Perkakas only accepts a single values array; Multiple arrays need to be spread manually instead. - Lodash accepts
nullandundefinedvalues for the array (and treats them as an empty array). In Perkakas this nullish value needs to be handled explicitly either by skipping the call todifferenceWith, or by coalescing the input to an empty array. - When the
comparatorparameter is not provided to the LodashdifferenceWithfunction (or is provided asundefined) it behaves like a call todifference.
Single exclusion array
// Lodash
_.differenceWith(DATA, values, comparator);
// Perkakas
differenceWith(DATA, values, comparator);
Multiple exclusion arrays
// Lodash
_.differenceWith(DATA, a, b, c, comparator);
// Perkakas
differenceWith(DATA, [...a, ...b, ...c], comparator);
Nullish inputs
// Lodash
_.differenceWith(DATA, values, comparator);
// Perkakas
isNonNullish(DATA) ? differenceWith(DATA, values, comparator) : [];
// Or
differenceWith(DATA ?? [], values, comparator);
Missing iteratee function
// Lodash
_.differenceWith(DATA, values);
// Convert to Lodash's `difference` and then refer to the `difference` migration
// docs
_.difference(DATA, values);
divide
Lodash divide → divide.
drop
Lodash drop → drop.
dropRight
Lodash dropRight → dropLast.
dropRightWhile
Lodash dropRightWhile → dropLastWhile.
dropWhile
Lodash dropWhile → dropWhile.
endsWith
Lodash endsWith → endsWith.
- Lodash supports an optional
positionparameter to check only part of the string, while Perkakas doesn't support this parameter. To replicate this behavior, usesliceStringto truncate the string before checking. - Lodash allows the suffix to be
undefined(converts to"undefined"string) but this is not supported by Perkakas and needs to be handled explicitly.
Basic usage
// Lodash
_.endsWith(input, suffix);
// Perkakas
endsWith(input, suffix);
// Native
input.endsWith(suffix);
Position parameter
// Lodash
_.endsWith(input, suffix, position);
// Perkakas
endsWith(sliceString(input, 0, position), suffix);
// Native
input.endsWith(suffix, position);
Undefined suffix
// Lodash
_.endsWith(input, suffix);
// Perkakas
suffix !== undefined && endsWith(input, suffix);
escape
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
escape is not needed in the vast majority of modern web development - all
modern frameworks and tools perform this kind of escaping (and more!)
automatically wherever it matters. If you are working on a low-level application
that writes directly to the DOM, prefer libraries that take a holistic approach
to this problem (like DOMPurify)
instead of basic string substitutions. In the rare case you must have an
escaping function, copy the reference code below as-is:
const ESCAPED: Readonly<Record<string, `&${string};`>> = {
// https://github.com/lodash/lodash/blob/main/lodash.js#L399-L403
'&': '&',
'<': '<',
'>': '>',
'"': '"',
'\'': ''',
};
const CHARS_RE = new RegExp(`[${Object.keys(ESCAPED).join('')}]`, 'g');
function escape(input: string): string {
return input.replace(CHARS_RE, (char) => ESCAPED[char]!);
}
escapeRegExp
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- Use the native
RegExp.escapeinstead. - When unavailable,
escapeRegExpcould also be replicated usingString.prototype.replaceto replace matches of/[\\^$.*+?()[\]{}|]/g(which matches on the characters: ^, $, ., *, +, ?, (), {}, , and |) with a backslash-escaped version of the match (using the"\\$&"replacement pattern).
// Lodash
_.escapeRegExp(input);
// Native
RegExp.escape(input);
// Or
input.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
every
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
Use the native JS Array.prototype.every instead.
// Lodash
every(DATA, predicate);
// Native
DATA.every(predicate);
filter
Lodash filter → filter.
find
Lodash find → find.
findIndex
Lodash findIndex → findIndex.
findLast
Lodash findLast → findLast.
findLastIndex
Lodash findLastIndex → findLastIndex.
flatMap
Lodash flatMap → flatMap.
flatMapDeep
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
This function could be recreated in Perkakas via a composition of
map, and flat with a large, const depth
parameter.
// Lodash
flatMapDeep(DATA, mappingFunction);
// Perkakas
flat(map(DATA, mappingFunction), 10 /* depth */);
// Or as a pipe
pipe(DATA, map(mappingFunction), flat(10 /* depth */));
flatMapDepth
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
This function could be recreated in Perkakas via map,
flatMap, or flat (or a combination of them,
depending on the parameters used).
// Lodash
flatMapDepth(DATA);
// Perkakas
flat(DATA);
// Lodash
flatMapDepth(DATA, mappingFunction);
// Perkakas
flatMap(DATA, mappingFunction);
// Lodash
flatMapDepth(DATA, mappingFunction, depth);
// Perkakas
flat(map(DATA, mappingFunction), depth);
// Or as a pipe
pipe(DATA, map(mappingFunction), flat(depth));
flatten
Lodash flatten → flat.
Either use with no params, or with a depth of 1.
// Lodash
flatten(DATA);
// Perkakas
flat(DATA);
flat(DATA, 1 /* depth */);
flattenDeep
Lodash flattenDeep → flat.
Unlike flatten in Lodash, the Perkakas flat function is always bound by the
depth param. To replicate the Lodash behavior use a high, const value.
Infinity and Number.MAX_INTEGER are not consts and would result in
inefficient typing.
// Lodash
flattenDeep(DATA);
// Perkakas
flat(DATA, 100); // ✅
flat(DATA, Infinity); // ❌
floor
Lodash floor → floor.
flow
Lodash flow → pipe.
forEach
Lodash forEach → forEach.
- In Lodash
forEachfirst checks if the data argument is array-like (by looking for alengthprop). In Perkakas the function only accepts arrays and calling it with an object would result in a TypeScript error. UseforEachObjinstead for those cases. - In Lodash
forEachstops iterating early when the callback returnsfalse. In Perkakas the callback cannot return a value (it must returnvoid) and this feature isn't available. You can build apipethat would replicate this logic.
Array
// Lodash
forEach([1, 2, 3], (item) => console.log(item));
// Perkakas
forEach([1, 2, 3], (item) => {
console.log(item);
});
Object
// Lodash
forEach({ a: 1, b: 2, c: 3 }, (value) => console.log(value));
// Perkakas
forEachObj({ a: 1, b: 2, c: 3 }, (value) => {
console.log(value);
});
Early exit
// Lodash
forEach([1, 2, 3], (item) => (item === 2 ? false : console.log(item)));
// Perkakas
// ❌ You can't return a value in a forEach callback in Perkakas:
forEach([1, 2, 3], (item) => (item === 2 ? false : console.log(item)));
// ✅ Instead you can rely on lazy evaluation of pipes:
pipe(
[1, 2, 3],
takeWhile((item) => item !== 2),
forEach((item) => {
console.log(item);
}),
);
forEachRight
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- This function could be replicated using a native JS
forloop. - If the order of iteration doesn't matter, you can use
forEachfor arrays, orforEachObjfor objects. - The order in which object properties are iterated over is well-defined, but
might not be the order you expect (MDN); nevertheless,
entriesmaintains the same order, and could be used to replicateforEachRight. - If the native solution doesn't suffice please open an issue at Perkakas's GitHub project so we can learn about your use-case.
Arrays
const DATA = [1, 2, 3];
// Lodash
forEachRight(DATA, (item) => (item === 2 ? false : console.log(item)));
// Native
for (const index = DATA.length - 1; index >= 0; index--) {
const item = DATA[item]!;
if (item === 2) {
break;
}
console.log(item);
}
Objects
const DATA = { a: 1, b: 2, c: 3 };
// Lodash
forEachRight(DATA, (value) => (value === 2 ? false : console.log(value)));
// Native
const keys = Object.keys(DATA);
for (const index = keys.length - 1; index >= 0; index--) {
const value = DATA[key]!;
if (value === 2) {
break;
}
console.log(value);
}
forOwn
Lodash forOwn → forEachObj.
fromPairs
Lodash fromPairs → fromEntries.
get
Lodash get → prop.
- In Lodash
gettakes an array where each element is a property in the path to a deeply-nested property (e.g.['a', 'b', '0', 'c']). In Perkakas the path is provided directly as a variadic arguments list. - In Lodash all property keys are
string, even when accessing numbers. In Perkakas the exact type of each prop should be used instead (e.g.,numberwhen accessing arrays). - Lodash also supports a string that defines the path to the deeply-nested
property (e.g.,
a.b[0].c). In Perkakas this isn't supported directly; instead, use the helper functionstringToPathto convert the path string to a path array, which could then be spread intoprop. - Lodash supports an optional last argument to
getwhich defines a default fallback in cases where the prop being accessed does not exist on the input object. To achieve this wrap your call topropwith a call todefaultTo, or use the native Nullish coalescing operator??instead if the fallback is of a different type.
Shallow Access
const DATA = { a: 123 };
// Lodash
_.get(DATA, ['a']);
// Perkakas
prop(DATA, 'a');
Array indices
const DATA = [1, 2, 3];
// Lodash
_.get(DATA, ['0']);
// Perkakas
prop(DATA, 0);
Path Arrays
const DATA = { a: [{ b: { c: 3 } }] };
// Lodash
_.get(DATA, ['a', '0', 'b', 'c']);
// Perkakas
prop(DATA, 'a', 0, 'b', 'c');
Path Strings
const DATA = { a: [{ b: { c: 3 } }] };
// Lodash
_.get(DATA, 'a[0].b.c');
// Perkakas
prop(DATA, ...stringToPath('a[0].b.c'));
Default Fallback
const DATA = { a: [] as Array<{ b: number }> };
// Lodash
_.get(DATA, ['a', '0', 'b'], 'default');
// Perkakas
defaultTo(prop(DATA, 'a', 0, 'b'), 123);
// Or natively
prop(DATA, 'a', 0, 'b') ?? 'default';
groupBy
Lodash groupBy → groupBy.
- When using
groupBywith a callback function use Perkakas'sgroupByfunction. - When using
groupByon an array of objects and with a property name use Perkakas'sgroupByPropinstead. - When using
groupByon an array of primitives (e.g.,string) and with a property name use Perkakas'sgroupBycomposed withprop.
Callback
const DATA = [6.1, 4.2, 6.3];
// Lodash
groupBy(DATA, Math.floor);
// Perkakas
groupBy(DATA, Math.floor);
Property Name (on objects)
const DATA = [
{ type: 'cat', name: 'felix' },
{ type: 'dog', toys: 3 },
];
// Lodash
groupBy(DATA, 'type');
// Perkakas
groupByProp(DATA, 'type');
Property Name (on primitives)
const DATA = ['one', 'two', 'three'];
// Lodash
groupBy(DATA, 'length');
// Perkakas
groupBy(DATA, prop('length'));
has
Lodash has → hasProp.
- Lodash's
hasaccepts deep paths ("a.b.c"or["a", "b", "c"]). Perkakas'shasProponly checks a single key.
// Lodash
has(obj, 'a');
// Perkakas
hasProp(obj, 'a');
hasIn
Lodash hasIn → hasProp.
- Lodash's
hasInuses theinoperator which returnstruefor inherited properties. Perkakas usesObject.hasOwnwhich ignores them. - Lodash's
hasInaccepts deep paths ("a.b.c"or["a", "b", "c"]). Perkakas'shasProponly checks a single key.
Own properties
// Lodash
hasIn(obj, 'a');
// Perkakas
hasProp(obj, 'a');
Inherited properties
// Lodash
hasIn({}, 'toString'); // => true
// Perkakas
hasProp({}, 'toString'); // => false
head
Lodash head → first.
identity
Lodash identity → identity.
In Perkakas the identity function is a factory that creates an identity
function; it needs to be called to be used.
// Lodash
map(identity);
// Perkakas
map(identity());
includes
Lodash includes → isIncludedIn.
- In Lodash
includesalso works on strings and objects; In Perkakas only arrays are supported. For objects, usevaluesfirst. For strings prefer the native JSString.prototype.includes. - The 3rd optional parameter
fromIndexisn't supported in Perkakas. You can replicate it's usage withdrop(for non-negative indices), ortakeLast(for non-positive indices). - The order of the parameters is flipped in Perkakas, the item is first, and then the array.
Arrays
// Lodash
includes([1, 2, 3], 1);
// Perkakas
isIncludedIn(1, [1, 2, 3]);
Objects
// Lodash
includes({ a: 1, b: 2 }, 1);
// Perkakas
isIncludedIn(1, values({ a: 1, b: 2 }));
Strings
const DATA = 'Hello, World!';
// Lodash
includes(DATA, 'lo');
// Native
DATA.includes('lo');
fromIndex: non-negative
// Lodash
includes([1, 2, 3], 1, 2);
// Perkakas
isIncludedIn(1, drop([1, 2, 3], 2));
fromIndex: non-positive
// Lodash
includes([1, 2, 3], 1, -2);
// Perkakas
isIncludedIn(1, takeLast([1, 2, 3], 2));
intersection
Lodash intersection → intersection.
In Perkakas intersection treats the inputs as multisets/bags which respect
item duplication, whereas in Lodash the result is always de-dupped. This only
matters for cases where both arrays might have duplicate values. To achieve
the same output pass the result to unique.
No duplicates
// Lodash
intersection([2, 1], [2, 3]);
// Perkakas
intersection([2, 1], [2, 3]);
With duplicates
// Lodash
intersection([1, 1], [1, 1]);
// Perkakas
unique(intersection([1, 1], [1, 1]));
intersectionWith
Lodash intersectionWith → intersectionWith.
invert
Lodash invert → invert.
invokeMap
Lodash invokeMap → map.
mapcan be used instead ofinvokeMapby using a callback functions instead of (string) function names.- If you need to invoke functions dynamically via a provided function name, narrow the name first (so you can handle errors explicitly), and then use the name to access the object.
No arguments
// Lodash
invokeMap(
[
[5, 1, 7],
[3, 2, 1],
],
'sort',
);
// Perkakas
map(
[
[5, 1, 7],
[3, 2, 1],
],
(array) => array.sort(),
);
With arguments
// Lodash
invokeMap([123, 456], String.prototype.split, '');
// Perkakas
map([123, 456], (num) => num.toString().split(''));
Dynamic
const DATA = [
{ foo: (x: number) => x + 1, bar: (x: number) => x - 1 },
{ foo: (x: number) => x * 2, bar: (x: number) => x / 2 },
] as const;
// Lodash
invokeMap(DATA, funcName, 3);
// Perkakas
if (funcName === 'foo' || funcName === 'bar') {
map(DATA, ({ [funcName]: func }) => func(3));
} else {
// Error!
}
// Or
map(DATA, (obj) => {
if (funcName === 'foo' || funcName === 'bar') {
return obj[funcName](3);
} else {
// Error
}
});
isArray
Lodash isArray → isArray.
isBoolean
Lodash isBoolean → isBoolean.
isDate
Lodash isDate → isDate.
isEmpty
Lodash isEmpty → isEmptyish.
isEqual
Lodash isEqual → isDeepEqual.
isError
Lodash isError → isError.
isFunction
Lodash isFunction → isFunction.
isMatch
Lodash isMatch → hasSubObject.
isNil
Lodash isNil → isNullish.
isNumber
Lodash isNumber → isNumber.
isObjectLike
Lodash isObjectLike → isObjectType.
isPlainObject
Lodash isPlainObject → isPlainObject.
isString
Lodash isString → isString.
isSymbol
Lodash isSymbol → isSymbol.
join
Lodash join → join.
kebabCase
Lodash kebabCase → toKebabCase.
- Lodash attempts pseudo-linguistic word splitting to handle special characters,
which might lead to inaccurate results. Perkakas uses a simpler word splitting
approach based on
type-fest's definition that should only be used for simple strings like identifiers and internal keys. For linguistic processing where language and locale nuances matter, use the built-inIntl.Segmenterinstead. - Lodash performs normalization on the input before splitting it, including
deburrand removing apostrophes. Perkakas's word splitting is simpler and doesn't include these normalizations, so they need to be done manually if required. - Lodash allows calling
kebabCasewithout any input, or with anundefinedinput, which isn't supported in Perkakas. Handle these cases before calling the function.
Simple strings
// Lodash
_.kebabCase(input);
// Perkakas
toKebabCase(input);
Normalized
// Lodash
_.kebabCase(input);
// Perkakas + Native
toKebabCase(
input
// "Promote" diacritics to be independent characters in the string.
.normalize('NFD')
// Remove apostrophes and all independent diacritic characters.
.replace(/['\u2019\u0300-\u036F]/g, ''),
);
Missing input
// Lodash
_.kebabCase();
_.kebabCase(input);
// Perkakas
('');
input !== undefined ? toKebabCase(input) : '';
// Or
toKebabCase(input ?? '');
keyBy
Lodash keyBy → indexBy.
keys
Lodash keys → keys.
last
Lodash last → last.
lowerCase
Lodash lowerCase → toTitleCase.
- Use
toLowerCaseon the results oftoTitleCase, which reformats the string into space-delimited words. - Lodash attempts pseudo-linguistic word splitting to handle special characters,
which might lead to inaccurate results. Perkakas uses a simpler word splitting
approach based on
type-fest's definition that should only be used for simple strings like identifiers and internal keys. For linguistic processing where language and locale nuances matter, use the built-inIntl.Segmenterinstead. - Lodash performs normalization on the input before splitting it, including
deburrand removing apostrophes. Perkakas's word splitting is simpler and doesn't include these normalizations, so they need to be done manually if required. - Lodash allows calling
lowerCasewithout any input, or with anundefinedinput, which isn't supported in Perkakas. Handle these cases before calling the function.
Basic usage
// Lodash
_.lowerCase(input);
// Perkakas
toLowerCase(toTitleCase(input));
Normalized
// Lodash
_.lowerCase(input);
// Perkakas + Native
toLowerCase(
toTitleCase(
input
// "Promote" diacritics to be independent characters in the string.
.normalize('NFD')
// Remove apostrophes and all independent diacritic characters.
.replace(/['\u2019\u0300-\u036F]/g, ''),
),
);
Missing input
// Lodash
_.lowerCase();
_.lowerCase(input);
// Perkakas
('');
input !== undefined ? toLowerCase(toTitleCase(input)) : '';
// Or
toLowerCase(toTitleCase(input ?? ''));
lowerFirst
Lodash lowerFirst → uncapitalize.
- For display purposes, prefer using CSS; use
::first-letterto target just the first letter of the word andtext-transform: lowercaseto lowercase it. - Lodash allows calling
lowerFirstwithout any input (or with anundefinedinput), which results in an empty string"". Perkakas'suncapitalizerequires a string input.
Basic usage
// Lodash
_.lowerFirst(input);
// Perkakas
uncapitalize(input);
CSS
// Lodash
<div>{_.lowerFirst(input)}</div>
// CSS
<style>
.lowerFirst::first-letter {
text-transform: lowercase;
}
</style>
<div class="lowerFirst">{input}</div>
Missing input data
// Lodash
_.lowerFirst();
_.lowerFirst(input);
// Perkakas
input !== undefined ? uncapitalize(input) : '';
// Or
uncapitalize(input ?? '');
map
Lodash map → map.
mapKeys
Lodash mapKeys → mapKeys.
mapValues
Lodash mapValues → mapValues.
max
Lodash max → firstBy.
Use identity as the criteria and "desc" as the direction.
// Lodash
max(DATA);
// Perkakas
firstBy(DATA, [identity(), 'desc']);
maxBy
Lodash maxBy → firstBy.
Use "desc" as the direction.
// Lodash
maxBy(DATA, criteria);
// Perkakas
firstBy(DATA, [criteria, 'desc']);
mean
Lodash mean → mean.
meanBy
Lodash meanBy → meanBy.
min
Lodash min → firstBy.
Use identity as the criteria.
// Lodash
min(DATA);
// Perkakas
firstBy(DATA, identity());
minBy
Lodash minBy → firstBy.
multiply
Lodash multiply → multiply.
negate
Lodash negate → isNot.
- Unlike Lodash's
negate,isNotpreserves type predicates: negating a type-guard returns a type-guard which excludes the guarded type, so the result offilterstays narrowed. - Lodash's
negateforwards every argument it receives to the predicate, and invokes it with the samethis.isNotonly takes a single-parameter predicate and passes just the first argument through; wrap multi-parameter functions with the native logical NOT operator!instead. - Lodash's
negatenegates the truthiness of any return value, whereasisNotrequires a predicate that returns aboolean. Compose withisTruthyto get the Lodash semantics.
Predicates
// Lodash
_.negate(predicate);
// Perkakas
isNot(predicate);
Filtering
// Lodash
_.filter(DATA, _.negate(_.isString));
// Perkakas
filter(DATA, isNot(isString));
Multiple parameters
// Lodash
_.negate(isGreaterThan);
// Native
(a: number, b: number) => !isGreaterThan(a, b);
Non-boolean predicates
// Lodash
_.negate(getLength);
// Perkakas
isNot(piped(getLength, isTruthy));
// Or directly via Native JS:
(data: string) => !getLength(data);
noop
Lodash noop → doNothing.
- If a return value is needed, use
constantwithundefined. - Otherwise, use
doNothing(which returnsvoid).
// Lodash
noop;
// Perkakas
constant(undefined);
doNothing();
nth
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- For index
0usefirst. - For index
-1uselast. - For arbitrary non-negative indices use the native JS
data[n]. - Or use
Array.prototype.atfor any index.
// Lodash
nth(DATA);
nth(DATA, 0);
// Perkakas
first(DATA);
// Lodash
nth(DATA, 1);
// Native
DATA[1];
DATA.at(1);
// Lodash
nth(DATA, -1);
// Perkakas
last(DATA);
// Lodash
nth(DATA, -2);
// Native
DATA.at(-2);
omit
Lodash omit → omit.
omitBy
Lodash omitBy → omitBy.
once
Lodash once → once.
orderBy
Lodash orderBy → sortBy.
Unlike in Lodash, in Perkakas the sort criteria can only be a callback (and not a prop name), it is paired with the order modifier ("asc"/"desc", if needed), and it's provided as a variadic argument and not two array arguments.
const DATA = [
{ user: 'fred', age: 48 },
{ user: 'barney', age: 34 },
{ user: 'fred', age: 40 },
{ user: 'barney', age: 36 },
];
// Lodash
orderBy(DATA, ['user', 'age'], ['asc', 'desc']);
// Perkakas
sortBy(DATA, prop('user'), [prop('age'), 'desc']);
pad
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- If
padis used with whitespace padding (which is also the default) in order to center text within an HTML component, prefer using CSS instead. Usewidthwith a length inchunits, andtext-align: center. - For other uses
padcould be replicated viapadStartandpadEnd, by computing the amount of effective padding you need on one side.
CSS
// Lodash
<pre>{_.pad(input, 20)}</pre>
// CSS
<div style="text-align:center;width:20ch">{input}</div>
Reference Implementation
function pad(data: string, length: number, padding = ' ') {
return data.padStart((length + data.length) / 2, padding).padEnd(length, padding);
}
padEnd
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- Use native
String.prototype.padEndinstead. - If
padEndis used with whitespace padding (which is also the default) in order to position text within an HTML component, prefer using CSS instead. Usewidthwith a length inchunits, andtext-align: left.
Basic Usage
// Lodash
_.padEnd(input, n);
_.padEnd(input, n, padding);
// Native
input.padEnd(n);
input.padEnd(n, padding);
CSS
// Lodash
<pre>{_.padEnd(input, 20)}</pre>
// CSS
<div style="text-align:left;width:20ch">{input}</div>
padStart
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- Use native
String.prototype.padStartinstead. - If
padStartis used with whitespace padding (which is also the default) in order to position text within an HTML component, prefer using CSS instead. Usewidthwith a length inchunits, andtext-align: right.
Basic Usage
// Lodash
_.padStart(input, n);
_.padStart(input, n, padding);
// Native
input.padStart(n);
input.padStart(n, padding);
CSS
// Lodash
<pre>{_.padStart(input, 20)}</pre>
// CSS
<div style="text-align:right;width:20ch">{input}</div>
parseInt
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- Use native
Number.parseIntinstead. - When used as a callback,
parseIntprotects against the index parameter unintentionally redefining theradix. To avoid this issue, wrap the callback with an arrow function.
Basic usage
// Lodash
_.parseInt(input);
_.parseInt(input, radix);
// Native
Number.parseInt(input);
Number.parseInt(input, radix);
Callback
// Lodash
data.map(_.parseInt);
// Native
data.map((item) => Number.parseInt(item, 10)); // ✅
data.map(Number.parseInt); // ❌
partial
Lodash partial → partialBind.
The placeholder argument _ is not supported. Some cases can be written as partialLastBind; otherwise, use an arrow function.
function greet(greeting, firstName, lastName) {
return `${greeting} ${firstName} ${lastName}`;
}
// Lodash
_.partial(greet, _, 'john', 'doe');
_.partial(greet, 'hi', _, 'doe');
// Perkakas
partialLastBind(greet, 'john', 'doe');
// (not supported)
// Native
(greeting) => greet(greeting, 'john', 'doe');
(firstName) => greet('hi', firstName, 'doe');
partialRight
Lodash partialRight → partialLastBind.
The placeholder argument _ is not supported. Some cases can be written as partialBind; otherwise, use an arrow function.
function greet(greeting, firstName, lastName) {
return `${greeting} ${firstName} ${lastName}`;
}
// Lodash
_.partialRight(greet, 'hi', 'john', _);
_.partialRight(greet, 'hi', _, 'doe');
// Perkakas
partialBind(greet, ['hi', 'john']);
// (not supported)
// Native
(lastName) => greet('hi', 'john', lastName);
(firstName) => greet('hi', firstName, 'doe');
partition
Lodash partition → partition.
pick
Lodash pick → pick.
pickBy
Lodash pickBy → pickBy.
pull
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
CAUTION: pull mutates the input array in-place! All functions in Perkakas
are pure and never mutate the
input. These mutations might introduce side-effects and implicit dependencies in
your codebase that need to be handled before migrating to Perkakas!
pullis equivalent to Lodash'sdifferencefunction. To migrate to Perkakas first migrate calls to Lodash_.differenceand then use the migration docs fordifferenceto complete the migration. IMPORTANT: The Perkakasdifferencefunction isn't a drop-in replacement for the Lodash_.differencefunction, we do not recommend migrating directly frompullto Perkakas'sdifference.pulltakes a variadic array of items to remove;differencetake an explicit array instead. You will need to wrap your items in an array when migrating.- If the mutability of the input array is desired, make sure the variable is
assignable (e.g., using
letinstead ofconst), and assign back the result ofdifferenceback to it. Note that if the input array is part of an object or nested array, you will need to manually reconstruct this outer containing object with the updated array manually. - If mutability wasn't desired, and instead the input was cloned (shallowly)
before calling
pull, that cloning is now redundant.
Exclusion Items
// pull
_.pull(DATA, a, b, c, ...additional);
// difference
_.difference(DATA, [a, b, c, ...additional]);
In-place mutation
// pull
const DATA = ['a', 'b', 'c', 'd'];
_.pull(DATA, ...values);
// difference
let DATA = ['a', 'b', 'c', 'd'];
DATA = _.difference(DATA, values);
Non-mutating usage
// pull
const pulled = _.pull([...DATA], ...values);
// difference
const pulled = _.difference(DATA, values);
Nested inside an object
// pull
const DATA = { a: ['a', 'b', 'c'], b: 'hello, world' };
_.pull(DATA.a, ...values);
// difference
let DATA = { a: ['a', 'b', 'c'], b: 'hello, world' };
DATA = { ...DATA, a: _.difference(DATA.a, values) };
pullAll
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
CAUTION: pullAll mutates the input array in-place! All functions in
Perkakas are pure and never
mutate the input. These mutations might introduce side-effects and implicit
dependencies in your codebase that need to be handled before migrating to
Perkakas!
pullAllis equivalent to Lodash'sdifferencefunction. To migrate to Perkakas first migrate calls to Lodash_.differenceand then use the migration docs fordifferenceto complete the migration. IMPORTANT: The Perkakasdifferencefunction isn't a drop-in replacement for the Lodash_.differencefunction, we do not recommend migrating directly frompullAllto Perkakas'sdifference.- If the mutability of the input array is desired, make sure the variable is
assignable (e.g., using
letinstead ofconst), and assign back the result ofdifferenceback to it. Note that if the input array is part of an object or nested array, you will need to manually reconstruct this outer object with the updated array manually. - If mutability wasn't desired, and instead the input was cloned (shallowly)
before calling
pullAll, that cloning is now redundant.
In-place mutation
// pullAll
const DATA = ['a', 'b', 'c', 'd'];
_.pullAll(DATA, values);
// difference
let DATA = ['a', 'b', 'c', 'd'];
DATA = _.difference(DATA, values);
Non-mutating usage
// pullAll
const pulled = _.pullAll([...DATA], values);
// difference
const pulled = _.difference(DATA, values);
Nested inside an object
// pullAll
const DATA = { a: ['a', 'b', 'c'], b: 'hello, world' };
_.pullAll(DATA.a, values);
// difference
let DATA = { a: ['a', 'b', 'c'], b: 'hello, world' };
DATA = { ...DATA, a: _.difference(DATA.a, values) };
pullAllBy
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
CAUTION: pullAllBy mutates the input array in-place! All functions in
Perkakas are pure and never
mutate the input. These mutations might introduce side-effects and implicit
dependencies in your codebase that need to be handled before migrating to
Perkakas!
pullAllByis equivalent to Lodash'sdifferenceByfunction. To migrate to Perkakas first migrate calls to Lodash_.differenceByand then use the migration docs fordifferenceByto complete the migration.- If the mutability of the input array is desired, make sure the variable is
assignable (e.g., using
letinstead ofconst), and assign back the result ofdifferenceByback to it. Note that if the input array is part of an object or nested array, you will need to manually reconstruct this outer object with the updated array manually. - If mutability wasn't desired, and instead the input was cloned (shallowly)
before calling
pullAllBy, that cloning is now redundant.
In-place mutation
// pullAllBy
const DATA = [{ x: 1 }, { x: 2 }, { x: 3 }];
_.pullAllBy(DATA, values, iteratee);
// differenceBy
let DATA = [{ x: 1 }, { x: 2 }, { x: 3 }];
DATA = _.differenceBy(DATA, values, iteratee);
Non-mutating usage
// pullAllBy
const pulled = _.pullAllBy([...DATA], values, iteratee);
// differenceBy
const pulled = _.differenceBy(DATA, values, iteratee);
Nested inside an object
// pullAllBy
const DATA = { items: [{ x: 1 }, { x: 2 }], meta: 'info' };
_.pullAllBy(DATA.items, values, iteratee);
// differenceBy
let DATA = { items: [{ x: 1 }, { x: 2 }], meta: 'info' };
DATA = { ...DATA, items: _.differenceBy(DATA.items, values, iteratee) };
pullAllWith
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
CAUTION: pullAllWith mutates the input array in-place! All functions in
Perkakas are pure and never
mutate the input. These mutations might introduce side-effects and implicit
dependencies in your codebase that need to be handled before migrating to
Perkakas!
pullAllWithis equivalent to Lodash'sdifferenceWithfunction. To migrate to Perkakas first migrate calls to Lodash_.differenceWithand then use the migration docs fordifferenceWithto complete the migration. IMPORTANT: The PerkakasdifferenceWithfunction isn't a drop-in replacement for the Lodash_.differenceWithfunction, we do not recommend migrating directly frompullAllWithto Perkakas'sdifferenceWith.- If the mutability of the input array is desired, make sure the variable is
assignable (e.g., using
letinstead ofconst), and assign back the result ofdifferenceWithback to it. Note that if the input array is part of an object or nested array, you will need to manually reconstruct this outer object with the updated array manually. - If mutability wasn't desired, and instead the input was cloned (shallowly)
before calling
pullAllWith, that cloning is now redundant.
In-place mutation
// pullAllWith
const DATA = [{ x: 1 }, { x: 2 }, { x: 3 }];
_.pullAllWith(DATA, values, comparator);
// differenceWith
let DATA = [{ x: 1 }, { x: 2 }, { x: 3 }];
DATA = _.differenceWith(DATA, values, comparator);
Non-mutating usage
// pullAllWith
const pulled = _.pullAllWith([...DATA], values, comparator);
// differenceWith
const pulled = _.differenceWith(DATA, values, comparator);
Nested inside an object
// pullAllWith
const DATA = { items: [{ x: 1 }, { x: 2 }], meta: 'info' };
_.pullAllWith(DATA.items, values, comparator);
// differenceWith
let DATA = { items: [{ x: 1 }, { x: 2 }], meta: 'info' };
DATA = { ...DATA, items: _.differenceWith(DATA.items, values, comparator) };
random
Lodash random → randomInteger.
- When Lodash's
randomfunction is called with a non-integer param it returns any number, not just integers (e.g. 2.5)! Perkakas'srandomIntegerfunction always returns integers, effectively rounding the parameters to fit the range of possible integers. - If you want to generate any number in the range (and not just integers) see the solutions provided in "You don't need Lodash".
- Lodash's
randomparameters are optional. In Perkakas all parameters are required.
Two integer params
// Lodash
random(1, 10);
// Perkakas
randomInteger(1, 10);
Single integer param
// Lodash
random(10);
// Perkakas
randomInteger(0, 10);
No params
// Lodash
random();
// Perkakas
randomInteger(0, 1);
Not supported: floating-point numbers
random(1.5, 3.5);
random(1.5);
random(10, true);
random(5, 10, true);
random(true);
range
Lodash range → range.
- When calling Lodash's
rangewith a single argument, an implicitstartof0is used. In Perkakas,startis always required. - When
start > end, Lodash infers a negative step (-1). Perkakas does not infer the step direction — a negativestepmust be provided explicitly, or the result is an empty array. - Lodash special-cases a
stepof0by computing the length of the output as ifstepwere1, and filling the output with thestartvalue. We throw aRangeErrorfor this case instead. - Lodash coerces
NaNto0(e.g.,_.range(NaN, 3)→[0, 1, 2]). Perkakas does not handleNaNinputs and their behavior is unspecified.
Implicit 0 start
// Lodash
_.range(5);
_.range(end);
// Perkakas
range(0, 5);
range(0, end);
With start
// Lodash
_.range(1, 5);
_.range(start, end);
// Perkakas
range(1, 5);
range(start, end);
With step
// Lodash
_.range(1, 20, 5);
_.range(start, end, step);
// Perkakas
range(1, { end: 20, step: 5 });
range(start, { end, step });
Descending range (start > end)
// Lodash
_.range(5, 1);
_.range(start, end);
// Perkakas
range(5, { end: 1, step: -1 });
range(start, { end, step: -1 });
Step of 0
// Lodash
_.range(1, 5, 0);
_.range(start, end, 0);
// Native
Array.from({ length: 4 }).fill(1);
Array.from({ length: Math.abs(end - start) }).fill(start);
rangeRight
No direct Perkakas helper. Use the notes below, or native JavaScript.
Lodash's rangeRight is equivalent to composing _.reverse on
_.range. Migrate first to that pattern, and then use the migration
guides for those functions to fully migrate to Perkakas.
// Lodash
_.rangeRight(start, end, step);
_.reverse(_.range(start, end, step));
reduce
Lodash reduce → reduce.
reduceRight
Lodash reduceRight → reduce.
Use reverse on the input.
// Lodash
reduceRight(DATA, reducer, accumulator);
// Perkakas
reduce(reverse(DATA), reducer, accumulator);
// Or with a pipe
pipe(DATA, reverse(), reduce(reducer, accumulator));
reject
Lodash reject → filter.
Wrap the callback with isNot.
// Lodash
reject(DATA, predicate);
// Perkakas
filter(DATA, isNot(predicate));
repeat
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
Use native String.prototype.repeat
instead.
// Lodash
_.repeat(input, n);
// Native
input.repeat(n);
replace
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- Use native
String.prototype.replaceinstead. - Lodash typing incorrectly allows calling
replacewith two arguments. These calls return strings as-is, while RegExp inputs are converted to strings.
Regular Usage
// Lodash
_.replace(input, pattern, replacement);
// Native
input.replace(pattern, replacement);
Invalid 2-parameter usage
// Lodash
_.replace(input, replacement);
_.replace(/pattern/, replacement);
// Native
input; // Returns input unchanged
String(/pattern/); // "/pattern/" - RegExp converted to string
reverse
Lodash reverse → reverse.
Lodash mutates the input but in Perkakas a new array is returned instead.
// Lodash
const DATA = [1, 2, 3];
reverse(DATA);
// Perkakas
let DATA = [1, 2, 3];
DATA = reverse(DATA);
round
Lodash round → round.
sample
Lodash sample → sample.
- The
samplefunction in Perkakas takes a required second parameter of the size of the sample. Use 1. - The Perkakas function returns an array; to extract the value either use
native destructuring or index access, or use
only. - For objects, first call
values. - If you are using
samplein an extremely hot path where you need the most efficient implementation possible, prefer a native implementation instead.
Simple
// Lodash
sample(DATA);
// Perkakas
only(sample(DATA, 1 /* size */));
// or with a pipe
pipe(DATA, sample(1 /* size */), only());
// Or with native accessors
sample(DATA, 1 /* size */)[0];
sample(DATA, 1 /* size */).at(0)!;
const [result] = sample(DATA, 1 /* size */);
Native
// Lodash
sample(DATA);
// Native
DATA[Math.floor(Math.random() * DATA.length)]!;
DATA.at(Math.floor(Math.random() * DATA.length))!;
sampleSize
Lodash sampleSize → sample.
- Unlike Lodash, In Perkakas the
samplefunction returns items in the same order they appear in the input. Use theshufflefunction if you also need to randomize their order. - The second parameter to
sampleSizein Lodash is optional, and defaults to 1 when not provided. In Perkakas this is a required param.
// Lodash
sampleSize(DATA);
// Perkakas
sample(DATA, 1);
// Lodash
sampleSize(DATA, 2);
// Perkakas
// ❌ The result isn't shuffled!
sample(DATA, 2);
// ✅ Add `shuffle` after `sample` (if order is important).
shuffle(sample(DATA, 2));
// Or with a pipe
pipe(DATA, sample(2), shuffle());
set
Lodash set → setPath.
- In Lodash the
setfunction supports two ways of defining the path parameter: a string representation of the path (similar to XPath: e.g.a.b[0].c), and an array representation of the path (e.g.['a', 'b', 0, 'c']). In Perkakas only the array representation is accepted. Use the helper functionstringToPathto translate string paths to array paths. - Unlike the Lodash
setfunction, In Perkakas the provided value must match the type of the prop at that path, the function does not support creating "sparse" objects. - For better type-safety, Perkakas offers two additional functions to handle
paths of length 1. Use
setto update an existing prop in an object (best type-safety and with IDE type-ahead), andaddPropto add a new prop to the object (or to override its type) - In Lodash
setmutates the input object. In Perkakas a new object is returned instead. The input object is never mutated.
let data = { a: 'hello', deep: [{ z: true }] };
// Lodash
set(data, 'a', 'world');
set(data, ['a'], 'foo');
set(data, 'a', 123);
set(data, ['a'], 456);
set(data, 'b', 123);
set(data, ['b'], 456);
set(data, 'deep[0].z', false);
set(data, ['deep', 0, 'z'], false);
// ❌ Not supported:
set(data, 'deep[0].newProp', 123);
set(data, ['deep', 0, 'newProp'], 123);
// Perkakas
data = set(data, 'a', 'world');
data = set(data, 'a', 'foo');
data = addProp(data, 'a', 123);
data = addProp(data, 'a', 456);
data = addProp(data, 'b', 123);
data = addProp(data, 'b', 456);
data = setPath(data, stringToPath('deep[0].z'), false);
data = setPath(data, ['deep', 0, 'z'], false);
shuffle
Lodash shuffle → shuffle.
size
Lodash size → length.
snakeCase
Lodash snakeCase → toSnakeCase.
- Lodash attempts pseudo-linguistic word splitting to handle special characters,
which might lead to inaccurate results. Perkakas uses a simpler word splitting
approach based on
type-fest's definition that should only be used for simple strings like identifiers and internal keys. For linguistic processing where language and locale nuances matter, use the built-inIntl.Segmenterinstead. - Lodash performs normalization on the input before splitting it, including
deburrand removing apostrophes. Perkakas's word splitting is simpler and doesn't include these normalizations, so they need to be done manually if required. - Lodash allows calling
snakeCasewithout any input, or with anundefinedinput, which isn't supported in Perkakas. Handle these cases before calling the function.
Simple strings
// Lodash
_.snakeCase(input);
// Perkakas
toSnakeCase(input);
Normalized
// Lodash
_.snakeCase(input);
// Perkakas + Native
toSnakeCase(
input
// "Promote" diacritics to be independent characters in the string.
.normalize('NFD')
// Remove apostrophes and all independent diacritic characters.
.replace(/['\u2019\u0300-\u036F]/g, ''),
);
Missing input
// Lodash
_.snakeCase();
_.snakeCase(input);
// Perkakas
('');
input !== undefined ? toSnakeCase(input) : '';
// Or
toSnakeCase(input ?? '');
some
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
Use the native JS Array.prototype.some instead.
// Lodash
some(DATA, predicate);
// Native
DATA.some(predicate);
sortBy
Lodash sortBy → sortBy.
The sortBy function in Perkakas does not support defining the sort criteria
using prop names, only callback functions. Also, the sort criteria is provided
as a variadic argument of sort criteria, and not a single array argument.
const DATA = [
{ user: 'fred', age: 48 },
{ user: 'barney', age: 34 },
{ user: 'fred', age: 40 },
{ user: 'barney', age: 36 },
];
// Lodash
sortBy(DATA, [
function (o) {
return o.user;
},
]);
sortBy(DATA, ['user', 'age']);
// Perkakas
sortBy(DATA, prop('user'));
sortBy(DATA, prop('user'), prop('age'));
sortedIndex
Lodash sortedIndex → sortedIndex.
sortedIndexBy
Lodash sortedIndexBy → sortedIndexBy.
sortedLastIndex
Lodash sortedLastIndex → sortedLastIndex.
sortedLastIndexBy
Lodash sortedLastIndexBy → sortedLastIndexBy.
sortedUniq
Lodash sortedUniq → unique.
Not provided by Perkakas.
There is no equivalent function in Perkakas because both unique and sortedUniq
run at O(n) time complexity. In space complexity (the amount of memory
needed to compute them), unique is O(k) where k is the number of unique
values in the array, while sortedUniq is O(1). In most real-life use cases,
the two implementations would have near-identical performance. We highly
recommend always using unique unless you know that the difference matters.
If you still need this algorithm, use filter with the
following predicate:
// Lodash
sortedUniq(DATA);
// Perkakas
unique(DATA);
filter(DATA, (item, index, array) => index === 0 || item !== array[index - 1]);
split
Lodash split → split.
- Lodash allows calling
splitwithout aseparatorparameter (or setting it toundefined) which returns the input string as a single-element array (the same happens whensplitis used as a callback). Perkakas needs the input wrapped in an array manually. - Lodash accepts
nullandundefinedinput values, converting them to empty strings. Perkakas needs these values handled separately. - When the
separatorparameter is"", Lodash uses complex logic to parse compound Unicode characters (like family emojis 👨👩👧👦 or flags with modifiers 🏳️🌈) as single graphemes. Perkakas uses nativeString.prototype.splitwhich splits these into component parts. UseIntl.Segmenterwithgranularity: "grapheme"for proper Unicode handling. This is rarely needed - simple Unicode characters (like non-Latin alphabets) work fine.
Basic usage
// Lodash
_.split(input, separator);
_.split(input, separator, limit);
// Perkakas
split(input, separator);
split(input, separator, limit);
Without separator
// Lodash
_.split(input);
_.map(data, _.split);
// Native
[input];
map(data, (item) => [item]);
Nullish inputs
// Lodash
_.split(input, separator);
_.split(input, separator, limit);
// Perkakas
input != null ? split(input, separator) : [''];
input != null ? split(input, separator, limit) : [''];
// Or
split(input ?? '', separator);
split(input ?? '', separator, limit);
Unicode grapheme splitting
// Lodash
_.split(input, '');
_.split(input, '', limit);
// Native
[...new Intl.Segmenter('en', { granularity: 'grapheme' }).segment(input)].map(
({ segment }) => segment,
);
[...new Intl.Segmenter('en', { granularity: 'grapheme' }).segment(input)]
.map(({ segment }) => segment)
.slice(0, limit);
startCase
Lodash startCase → toTitleCase.
- Lodash attempts pseudo-linguistic word splitting to handle special characters,
which might lead to inaccurate results. Perkakas uses a simpler word splitting
approach based on
type-fest's definition that should only be used for simple strings like identifiers and internal keys. For linguistic processing where language and locale nuances matter, use the built-inIntl.Segmenterinstead. - Perkakas treats consecutive uppercase characters differently from Lodash. Use
{ preserveConsecutiveUppercase: false }as the second parameter to get the same results. - Lodash performs normalization on the input before splitting it, including
deburrand removing apostrophes. Perkakas's word splitting is simpler and doesn't include these normalizations, so they need to be done manually if required. - Lodash allows calling
startCasewithout any input, or with anundefinedinput, which isn't supported in Perkakas. Handle these cases before calling the function.
Simple strings
// Lodash
_.startCase(input);
// Perkakas
toTitleCase(input, { preserveConsecutiveUppercase: false });
Normalized
// Lodash
_.startCase(input);
// Perkakas + Native
toTitleCase(
input
// "Promote" diacritics to be independent characters in the string.
.normalize('NFD')
// Remove apostrophes and all independent diacritic characters.
.replace(/['\u2019\u0300-\u036F]/g, ''),
{ preserveConsecutiveUppercase: false },
);
Missing input
// Lodash
_.startCase();
_.startCase(input);
// Perkakas
('');
input !== undefined
? toTitleCase(input, { preserveConsecutiveUppercase: false })
: '';
// Or
toTitleCase(input ?? '', { preserveConsecutiveUppercase: false });
startsWith
Lodash startsWith → startsWith.
- Lodash supports an optional
positionparameter to check from a specific index, while Perkakas only checks from the beginning of the string. ComposesliceStringwithstartsWithto replicate this behavior. - Lodash allows calling the function without a
targetparameter (or usingundefinedfor it). These would result in the function always returningtrue. In Perkakas thetargetis required and handlingundefinedshould be done before callingstartsWith. - Lodash also allows calling the function without an
inputparameter (or usingundefinedfor it). These would result in the function always returningfalse(unlesstargetis also""orundefined). This parameter is also required in Perkakas; handle theundefinedexplicitly.
Basic usage
// Lodash
_.startsWith(input, target);
// Perkakas
startsWith(input, target);
Position parameter
// Lodash
_.startsWith(input, target, position);
// Perkakas
startsWith(sliceString(input, position), target);
Missing parameters
// Lodash
_.startsWith();
_.startsWith(input);
// Native
true;
true;
Undefined target
// Lodash
_.startsWith(input, target);
// Perkakas
target === undefined || startsWith(input, target);
// Or
startsWith(input, target ?? '');
Undefined input
// Lodash
_.startsWith(input, target);
// Perkakas
startsWith(input ?? '', target);
// Or (if you know target is not `undefined` or `""`)
input !== undefined && startsWith(input, target);
subtract
Lodash subtract → subtract.
sum
Lodash sum → sum.
sumBy
Lodash sumBy → sumBy.
take
Lodash take → take.
takeRight
Lodash takeRight → takeLast.
takeRightWhile
Lodash takeRightWhile → takeLastWhile.
takeWhile
Lodash takeWhile → takeWhile.
tap
Lodash tap → tap.
template
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
Lodash's template is a complex templating engine that compiles templates into
executable JavaScript functions. Due to its complexity and security
implications, it is outside the scope that Perkakas offers.
- In most cases it can be substituted by native template literals.
- In many other cases it is redundant with the templating capabilities of frameworks such as React, Express, Vue, Fastify, and many others.
- For the most complex cases there are dedicated libraries that offer robust templating features (e.g., Handlebars).
Template Literals
// Lodash
const greet = _.template('Hello <%= firstName %> <%= lastName %>!');
// Template literals
function greet({
firstName,
lastName,
}: {
firstName: string;
lastName: string;
}) {
return `Hello ${firstName} ${lastName}!`;
}
HTML
// Lodash
const bold = _.template('<b><%- value %></b>');
bold({ value: '<script>' });
// React
function Bold({ value }: { value: string }) {
return <b>{value}</b>;
}
<Bold value="<script>" />;
With complex logic
// Lodash
const list = _.template(
'<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>',
);
list({ users: ['fred', 'barney'] });
// React
function List({ users }: { users: Array<string> }) {
return users.map((user) => <li key={user}>{user}</li>);
}
<List users={['fred', 'barney']} />;
throttle
Lodash throttle → funnel.
throttlecan be implemented using thefunnelutility. A reference implementation is provided below, and a more expanded version with inline documentation and tests is available in the test filefunnel.lodash-throttle.test.ts.- A more complete reference implementation that also maintains Lodash's
capability to store the callback's return value is available below, and in
funnel.lodash-throttle-with-cached-value.test.ts. - These implementations can be copied as-is into your project, but might contain
redundant parts which are not relevant for your specific use cases. By
inlining only the parts you need you can take advantage of capabilities not
available in Lodash. To copy the expanded versions from the test files
instead, take everything between the
REFERENCE STARTandREFERENCE ENDmarkers.
Reference
import { funnel } from '@vinicunca/perkakas';
function throttle<F extends (...args: any) => void>(
func: F,
wait = 0,
{
leading = true,
trailing = true,
}: { readonly leading?: boolean; readonly trailing?: boolean } = {},
) {
const {
call,
isIdle: _isIdle,
...rest
} = funnel(
() => {
if (leading || trailing) {
func();
}
},
{
minQuietPeriodMs: wait,
maxBurstDurationMs: wait,
...(trailing
? leading
? { triggerAt: 'both' }
: { triggerAt: 'end' }
: { triggerAt: 'start' }),
},
);
return Object.assign(call, rest);
}
With call arguments
import { funnel } from '@vinicunca/perkakas';
function throttle<F extends (...args: any) => void>(
func: F,
wait = 0,
{
leading = true,
trailing = true,
}: { readonly leading?: boolean; readonly trailing?: boolean } = {},
) {
const {
call,
isIdle: _isIdle,
...rest
} = funnel(
(args: Parameters<F>) => {
if (leading || trailing) {
func(...args);
}
},
{
reducer: (_, ...args: Parameters<F>) => args,
minQuietPeriodMs: wait,
maxBurstDurationMs: wait,
...(trailing
? leading
? { triggerAt: 'both' }
: { triggerAt: 'end' }
: { triggerAt: 'start' }),
},
);
return Object.assign(call, rest);
}
With cached value
import { funnel } from '@vinicunca/perkakas';
function throttle<F extends (...args: any) => any>(
func: F,
wait = 0,
{
leading = true,
trailing = true,
}: { readonly leading?: boolean; readonly trailing?: boolean } = {},
) {
let cachedValue: ReturnType<F> | undefined;
const { call, flush, cancel } = funnel(
(args: Parameters<F>) => {
if (leading || trailing) {
cachedValue = func(...args) as ReturnType<F>;
}
},
{
reducer: (_, ...args: Parameters<F>) => args,
minQuietPeriodMs: wait,
maxBurstDurationMs: wait,
...(trailing
? leading
? { triggerAt: 'both' }
: { triggerAt: 'end' }
: { triggerAt: 'start' }),
},
);
return Object.assign(
(...args: Parameters<F>) => {
call(...args);
return cachedValue;
},
{
flush: () => {
flush();
return cachedValue;
},
cancel,
},
);
}
times
Lodash times → times.
toLower
Lodash toLower → toLowerCase.
- For display purposes, prefer using CSS
text-transform: lowercase. - Lodash allows calling
toLowerwithout any input, or with anundefinedinput, which isn't supported in Perkakas. Handle these cases before calling the function.
Basic usage
// Lodash
_.toLower(input);
// Perkakas
toLowerCase(input);
CSS
// Lodash
<div>{_.toLower(input)}</div>
// CSS
<div style="text-transform:lowercase">{input}</div>
Missing input
// Lodash
_.toLower();
_.toLower(input);
// Perkakas
('');
input !== undefined ? toLowerCase(input) : '';
// Or
toLowerCase(input ?? '');
toPairs
Lodash toPairs → entries.
toPath
Lodash toPath → stringToPath.
toUpper
Lodash toUpper → toUpperCase.
- For display purposes, prefer using CSS
text-transform: uppercase. - Lodash allows calling
toUpperwithout any input, or with anundefinedinput, which isn't supported in Perkakas. Handle these cases before calling the function.
Basic usage
// Lodash
_.toUpper(input);
// Perkakas
toUpperCase(input);
CSS
// Lodash
<div>{_.toUpper(input)}</div>
// CSS
<div style="text-transform:uppercase">{input}</div>
Missing input
// Lodash
_.toUpper();
_.toUpper(input);
// Perkakas
('');
input !== undefined ? toUpperCase(input) : '';
// Or
toUpperCase(input ?? '');
transform
Lodash transform → reduce.
trim
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- When the second
charactersparameter is not provided to Lodash (or when it isundefined), all whitespace characters would be trimmed. This is the same as the nativeString.prototype.trim. - The native
trimdoesn't support the additionalcharactersparameter that allows changing the trimmed characters. Instead, create a regex that would matchcharactersanchored to either the start or the end of the string (^[${characters}]+|[${characters}]+$) and then useString.prototype.replaceto replace them with the empty string ("") and use thegRegExp flag so that it matches both the start and the end sequences. You might need to useRegExp.escapeoncharactersif they might contain special regex characters. - Lodash does complex grapheme parsing, but this is usually not needed unless
the
charactersparameter itself contains complex Unicode graphemes (like family emojis 👨👩👧👦 or flags with modifiers 🏳️🌈 that you want to trim). In these cases useIntl.Segmenterwithgranularity: "grapheme"to split the string, then use Perkakas'sdropWhile(for left trimming) anddropLastWhile(for right trimming), and rejoin the array back withjoin. - Lodash allows calling
trimwithout any input (or with anundefinedinput), which results in an empty string"". This requires explicit handling in replacements.
Whitespaces
// Lodash
_.trim(input);
// Native
input.trim();
Callback
// Lodash
_.map(data, _.trim);
// Native
data.map(String.prototype.trim);
Characters
// Lodash
_.trim(input, characters);
// Native
const escaped = RegExp.escape(characters);
input.replace(new RegExp(`^[${escaped}]+|[${escaped}]+$`, 'g'), '');
Graphemes
// Lodash
_.trim(input, characters);
// Perkakas
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
const inputGraphemes = Array.from(segmenter.segment(input), prop('segment'));
const graphemes = Array.from(segmenter.segment(characters), prop('segment'));
join(
dropLastWhile(
dropWhile(inputGraphemes, isIncludedIn(graphemes)),
isIncludedIn(graphemes),
),
'',
);
Missing input data
// Lodash
_.trim();
_.trim(input);
// Native
('');
input?.trim() ?? '';
trimEnd
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- When the second
charactersparameter is not provided to Lodash (or when it isundefined), all whitespace characters would be trimmed. This is the same as the nativeString.prototype.trimEnd. - The native
trimEnddoesn't support the additionalcharactersparameter that allows changing the trimmed characters. Instead, create a regex that would matchcharactersanchored to the end of the string ([${characters}]+$) and then useString.prototype.replaceto replace them with the empty string (""). You might need to useRegExp.escapeoncharactersif they might contain special regex characters. - Lodash does complex grapheme parsing, but this is usually not needed unless
the
charactersparameter itself contains complex Unicode graphemes (like family emojis 👨👩👧👦 or flags with modifiers 🏳️🌈 that you want to trim). In these cases useIntl.Segmenterwithgranularity: "grapheme"to split the string, then use Perkakas'sdropLastWhile, and rejoin the array back withjoin. - Lodash allows calling
trimEndwithout any input (or with anundefinedinput), which results in an empty string"". This requires explicit handling in replacements.
Whitespaces
// Lodash
_.trimEnd(input);
// Native
input.trimEnd();
Callback
// Lodash
_.map(data, _.trimEnd);
// Native
data.map(String.prototype.trimEnd);
Characters
// Lodash
_.trimEnd(input, characters);
// Native
input.replace(new RegExp(`[${RegExp.escape(characters)}]+$`), '');
Graphemes
// Lodash
_.trimEnd(input, characters);
// Perkakas
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
const inputGraphemes = Array.from(segmenter.segment(input), prop('segment'));
const graphemes = Array.from(segmenter.segment(characters), prop('segment'));
join(dropLastWhile(inputGraphemes, isIncludedIn(graphemes)), '');
Missing input data
// Lodash
_.trimEnd();
_.trimEnd(input);
// Native
('');
input?.trimEnd() ?? '';
trimStart
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
- When the second
charactersparameter is not provided to Lodash (or when it isundefined), all whitespace characters would be trimmed. This is the same as the nativeString.prototype.trimStart. - The native
trimStartdoesn't support the additionalcharactersparameter that allows changing the trimmed characters. Instead, create a regex that would matchcharactersanchored to the start of the string (^[${characters}]+) and then useString.prototype.replaceto replace them with the empty string (""). You might need to useRegExp.escapeoncharactersif they might contain special regex characters. - Lodash does complex grapheme parsing, but this is usually not needed unless
the
charactersparameter itself contains complex Unicode graphemes (like family emojis 👨👩👧👦 or flags with modifiers 🏳️🌈 that you want to trim). In these cases useIntl.Segmenterwithgranularity: "grapheme"to split the string, then use Perkakas'sdropWhile, and then rejoin the array back withjoin. - Lodash allows calling
trimStartwithout any input (or with anundefinedinput), which results in an empty string"". This requires explicit handling in replacements.
Whitespaces
// Lodash
_.trimStart(input);
// Native
input.trimStart();
Callback
// Lodash
_.map(data, _.trimStart);
// Native
data.map(String.prototype.trimStart);
Characters
// Lodash
_.trimStart(input, characters);
// Native
input.replace(new RegExp(`^[${RegExp.escape(characters)}]+`), '');
Graphemes
// Lodash
_.trimStart(input, characters);
// Perkakas
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
const inputGraphemes = Array.from(segmenter.segment(input), prop('segment'));
const graphemes = Array.from(segmenter.segment(characters), prop('segment'));
join(dropWhile(inputGraphemes, isIncludedIn(graphemes)), '');
Missing input data
// Lodash
_.trimStart();
_.trimStart(input);
// Native
('');
input?.trimStart() ?? '';
truncate
Lodash truncate → truncate.
- Perkakas requires an explicit
lengthparameter, while Lodash defaults to30when it isn't provided. - Lodash computes the length of the
inputandomissionstrings in graphemes, unlike Perkakas that counts Unicode characters. In the vast majority of cases these are identical, but when these strings contain complex Unicode characters (like family emojis 👨👩👧👦 or flags with modifiers 🏳️🌈) the input might not be truncated at the same index. - The Perkakas function would never return a string longer than
length, opting to truncate theomissionitself when it's too long. Lodash will never truncate theomissionand therefore might return an output that exceedslength.
Default length
// Lodash
_.truncate(input);
// Perkakas
truncate(input, 30);
Custom length
// Lodash
_.truncate(input, { length });
// Perkakas
truncate(input, length);
With options
// Lodash
_.truncate(input, { omission, separator });
_.truncate(input, { length, omission, separator });
// Perkakas
truncate(input, 30, { omission, separator });
truncate(input, length, { omission, separator });
unescape
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
unescape is rarely needed in modern web development. The main use case is
processing HTML documents/snippets. For comprehensive HTML processing, prefer
dedicated HTML/XML parsing libraries. In the rare case you must have an
unescaping function, copy the reference code below as-is:
const UNESCAPED: Readonly<Record<`&${string};`, string>> = {
// https://github.com/lodash/lodash/blob/main/lodash.js#L408-L412
'&': '&',
'<': '<',
'>': '>',
'"': '"',
''': '\'',
};
const ENTITIES_RE = new RegExp(`${Object.keys(UNESCAPED).join('|')}`, 'g');
function unescape(input: string): string {
return input.replace(ENTITIES_RE, (entity) => UNESCAPED[entity]!);
}
uniq
Lodash uniq → unique.
uniqBy
Lodash uniqBy → uniqueBy.
uniqWith
Lodash uniqWith → uniqueWith.
upperCase
Lodash upperCase → toTitleCase.
- Use
toUpperCaseon the results oftoTitleCase, which reformats the string into space-delimited words. - Lodash attempts pseudo-linguistic word splitting to handle special characters,
which might lead to inaccurate results. Perkakas uses a simpler word splitting
approach based on
type-fest's definition that should only be used for simple strings like identifiers and internal keys. For linguistic processing where language and locale nuances matter, use the built-inIntl.Segmenterinstead. - Lodash performs normalization on the input before splitting it, including
deburrand removing apostrophes. Perkakas's word splitting is simpler and doesn't include these normalizations, so they need to be done manually if required. - Lodash allows calling
upperCasewithout any input, or with anundefinedinput, which isn't supported in Perkakas. Handle these cases before calling the function.
Basic usage
// Lodash
_.upperCase(input);
// Perkakas
toUpperCase(toTitleCase(input));
Normalized
// Lodash
_.upperCase(input);
// Perkakas + Native
toUpperCase(
toTitleCase(
input
// "Promote" diacritics to be independent characters in the string.
.normalize('NFD')
// Remove apostrophes and all independent diacritic characters.
.replace(/['\u2019\u0300-\u036F]/g, ''),
),
);
Missing input
// Lodash
_.upperCase();
_.upperCase(input);
// Perkakas
('');
input !== undefined ? toUpperCase(toTitleCase(input)) : '';
// Or
toUpperCase(toTitleCase(input ?? ''));
upperFirst
Lodash upperFirst → capitalize.
- For display purposes, prefer using CSS; use
::first-letterto target just the first letter of the word andtext-transform: uppercaseto capitalize it. - Lodash allows calling
upperFirstwithout any input (or with anundefinedinput), which results in an empty string"". Perkakas'scapitalizerequires a string input.
Basic usage
// Lodash
_.upperFirst(input);
// Perkakas
capitalize(input);
CSS
// Lodash
<div>{_.upperFirst(input)}</div>
// CSS
<style>
.upperFirst::first-letter {
text-transform: uppercase;
}
</style>
<div class="upperFirst">{input}</div>
Missing input data
// Lodash
_.upperFirst();
_.upperFirst(input);
// Perkakas
input !== undefined ? capitalize(input) : '';
// Or
capitalize(input ?? '');
values
Lodash values → values.
without
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
withoutis equivalent to Lodash'sdifferencefunction. To migrate to Perkakas first migrate calls to Lodash_.differenceand then use the migration docs fordifferenceto complete the migration. IMPORTANT: The Perkakasdifferencefunction isn't a drop-in replacement for the Lodash_.differencefunction, we do not recommend migrating directly fromwithoutto Perkakas'sdifference.withouttakes variadic arguments;differencetakes an explicit array. You will need to wrap your items in an array when migrating.
// without
_.without(DATA, a, b, c, ...additional);
// difference
_.difference(DATA, [a, b, c, ...additional]);
words
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
wordsis often used to convert between different cases of identifiers and keys. UsetoCamelCase,toKebabCase,toSnakeCase, ortoTitleCaseinstead.- If
wordsis used for simple splitting tasks, it can often be replaced withString.prototype.splitusing simple regular expressions like/\s+/,/\W+/,/[\p{Z}\p{P}]+/u, or ones tailored specifically for your use-case. - Lodash performs a lot of pseudo-linguistic heuristics in order to detect
special characters like diacritics, emojis, and complex graphemes. If you need
accurate language-aware splitting of words, prefer
Intl.Segmenterwithgranularity: "word". - When provided with the optional
patternparameter,wordsdefers the call toString.prototype.matchas-is.
Case conversion
// Lodash
_.words(input).map(_.toLowerCase).join('-');
_.words(input).map(_.toLowerCase).join('_');
_.words(input).map(_.capitalize).join(' ');
_.words(input).map(_.toUpperCase).join('-');
_.words(input).map(_.toUpperCase).join('_');
// Perkakas
toKebabCase(input);
toSnakeCase(input);
toTitleCase(input, { preserveConsecutiveUppercase: false });
toUpperCase(toKebabCase(input));
toUpperCase(toSnakeCase(input));
Naive splitting
// Lodash
_.words(input);
// Perkakas
split(input, /\s+/); // spaces
split(input, /\W+/); // spaces, punctuation
split(input, /[\p{Z}\p{P}]+/u); // support for unicode spaces and punctuation.
Proper NLP
// Lodash
_.words(input);
// Native
const segmenter = new Intl.Segmenter('en', { granularity: 'word' });
Array.from(segmenter.segment(input)).map(({ segment }) => segment);
Custom pattern
// Lodash
_.words(input, pattern);
// Native
input.match(pattern);
zip
Lodash zip → zip.
zipObject
No direct Perkakas helper. Use the notes below, or native JavaScript.
Not provided by Perkakas.
Compose zip and fromEntries:
// Lodash
zipObject(keys, values);
// Perkakas
fromEntries(zip(keys, values));
zipWith
Lodash zipWith → zipWith.