---
title: "Coming from Lodash"
description: "Map Lodash functions to @vinicunca/perkakas, with notes where the APIs differ."
canonical_url: "https://vinicunca.dev/perkakas/migrate/lodash"
---
# Coming from Lodash

> Map Lodash functions to @vinicunca/perkakas, with notes where the APIs differ.

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`](https://vinicunca.dev/perkakas/functions#add).

## assign

Lodash `assign` → [`merge`](https://vinicunca.dev/perkakas/functions#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 `assign` can be used to merge any number of objects by accepting a
variadic argument list. In Perkakas, if you are merging only 2 objects use
`merge`, but if you need to merge more than 2 objects use
[`mergeAll`](https://vinicunca.dev/perkakas/functions#mergeAll) instead, which takes an *Array* of objects.

```ts
// 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`](https://vinicunca.dev/perkakas/functions#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](https://github.com/sindresorhus/type-fest/blob/main/source/words.d.ts)
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-in [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
instead.
- 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
[`deburr`](#deburr) and 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 `camelCase` without any input, or with an `undefined`
input, which isn't supported in Perkakas. Handle these cases before calling the
function.

#### Simple strings

```ts
// Lodash
_.camelCase(input);

// Perkakas
toCamelCase(input, { preserveConsecutiveUppercase: false });
```

#### Normalized

```ts
// 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

```ts
// Lodash
_.camelCase();
_.camelCase(input);

// Perkakas
('');
input !== undefined
  ? toCamelCase(input, { preserveConsecutiveUppercase: false })
  : '';

// Or
toCamelCase(input ?? '', { preserveConsecutiveUppercase: false });
```

## capitalize

Lodash `capitalize` → [`capitalize`](https://vinicunca.dev/perkakas/functions#capitalize).

- Lodash converts the entire string to lowercase first, while Perkakas only
capitalizes the first character. To match Lodash behavior, call
[`toLowerCase`](https://vinicunca.dev/perkakas/functions#toLowerCase) first.
- For display purposes, prefer using CSS; use [`text-transform: lowercase`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform#lowercase)
on the element, then use [`::first-letter`](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter)
to target just the first letter of the word, and use [`text-transform: uppercase`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform#uppercase)
to capitalize it.

#### Runtime

```ts
// Lodash
_.capitalize(input);

// Perkakas
capitalize(toLowerCase(input));
```

#### CSS

```tsx
// 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`](https://vinicunca.dev/perkakas/functions#ceil).

## chunk

Lodash `chunk` → [`chunk`](https://vinicunca.dev/perkakas/functions#chunk).

## clamp

Lodash `clamp` → [`clamp`](https://vinicunca.dev/perkakas/functions#clamp).

## cloneDeep

Lodash `cloneDeep` → [`clone`](https://vinicunca.dev/perkakas/functions#clone).

## compact

Lodash `compact` → [`filter`](https://vinicunca.dev/perkakas/functions#filter).

Use [`isTruthy`](https://vinicunca.dev/perkakas/functions#isTruthy) as the predicate.

```ts
// Lodash
compact(DATA);

// Perkakas
filter(DATA, isTruthy);
```

## concat

Lodash `concat` → [`concat`](https://vinicunca.dev/perkakas/functions#concat).

## cond

Lodash `cond` → [`conditional`](https://vinicunca.dev/perkakas/functions#conditional).

## constant

Lodash `constant` → [`constant`](https://vinicunca.dev/perkakas/functions#constant).

## countBy

Lodash `countBy` → [`countBy`](https://vinicunca.dev/perkakas/functions#countBy).

## debounce

Lodash `debounce` → [`funnel`](https://vinicunca.dev/perkakas/functions#funnel).

- `debounce` can be implemented using the `funnel` utility. A reference
implementation is provided below, and a more expanded version with inline
documentation and tests is available in the test file [`funnel.lodash-debounce.test.ts`](https://github.com/remeda/remeda/blob/main/packages/remeda/src/funnel.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`](https://github.com/remeda/remeda/blob/main/packages/remeda/src/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 START` and `REFERENCE END`
markers.

#### Reference

```ts
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

```ts
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

```ts
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., Æ, Ĳ, Œ, and ŉ).
- 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.Collator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator)
with `sensitivity: 'base'` to compare them while ignoring accents and case.
- When **simplifying** strings, prefer [`normalize("NFD")`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)
to decompose characters, then use [`replace`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
to remove [diacritics](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). This solution is more accurate, but is not a drop-in replacement for `deburr` because 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

```ts
// Lodash
_.deburr('café') === 'cafe';

// Native JS
const collator = new Intl.Collator('en', { sensitivity: 'base' });
collator.compare('café', 'cafe') === 0;
```

#### Simplifying

```ts
// Lodash
_.deburr(input);

// Native JS
input
  .normalize('NFD')
  .replace(
    /[\u0300-\u036F]/g /* The "Combining Diacritical Marks" Unicode block. */,
    '',
  );
```

## defaultTo

Lodash `defaultTo` → [`defaultTo`](https://vinicunca.dev/perkakas/functions#defaultTo).

- `Number.NaN` is not considered nullish and would not result in returning the
default value. Use [`when`](https://vinicunca.dev/perkakas/functions#when) with the built-in [`Number.isNaN`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN)
and a [`constant`](https://vinicunca.dev/perkakas/functions#constant) for 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 `??`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing)
directly, or use [`when`](https://vinicunca.dev/perkakas/functions#when) with [`isNullish`](https://vinicunca.dev/perkakas/functions#isNullish) and
[`constant`](https://vinicunca.dev/perkakas/functions#constant).

#### Nullish

```ts
const DATA: number | undefined | null;

// Lodash
_.defaultTo(DATA, 456);

// Perkakas
defaultTo(DATA, 456);
```

#### NaN

```ts
const DATA = Number.NaN;

// Lodash
_.defaultTo(DATA, 10);

// Perkakas
when(DATA, Number.isNaN, constant(10));
```

#### Both

```ts
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

```ts
const DATA: string | null | undefined;

// Lodash
_.defaultTo(DATA, 123);

// Perkakas
when(DATA, isNullish, constant(123));

// or natively
DATA ?? 123;
```

## difference

Lodash `difference` → [`difference`](https://vinicunca.dev/perkakas/functions#difference).

- Perkakas's `difference` treats 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`](https://vinicunca.dev/perkakas/functions#filter), [`isNot`](https://vinicunca.dev/perkakas/functions#isNot),
and [`isIncludedIn`](https://vinicunca.dev/perkakas/functions#isIncludedIn) to replicate the Lodash semantics.
- Lodash's `difference` accepts multiple values arrays as separate arguments and
flattens them, while Perkakas's `difference` takes a single values array. When
migrating these arrays need to be flattened or spread into a single array
manually.
- Lodash supports calling `difference` trivially, 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 `null` and `undefined` values 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 to `difference`, or by coalescing the input to an
empty array.

#### Without duplicate values (unique)

```ts
// Lodash
_.difference(uniqueData, uniqueValues);

// Perkakas
difference(uniqueData, uniqueValues);
```

#### With duplicate values

```ts
// 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

```ts
// Lodash
_.difference(DATA, a, b, c);

// Perkakas
difference(DATA, [...a, ...b, ...c]);
```

#### Missing 2nd (exclusions) parameter

```ts
// Lodash
_.difference(DATA);

// Perkakas
difference(DATA, []);

// Or directly via Native JS:
[...DATA];
```

#### Nullish inputs

```ts
// Lodash
_.difference(DATA, values);

// Perkakas
isNonNullish(DATA) ? difference(DATA, values) : [];

// Or
difference(DATA ?? [], values);
```

## differenceBy

Lodash `differenceBy` → [`filter`](https://vinicunca.dev/perkakas/functions#filter).

*Not provided by Perkakas.*

- Use a composition based on [`filter`](https://vinicunca.dev/perkakas/functions#filter), [`isNot`](https://vinicunca.dev/perkakas/functions#isNot)[`isIncludedIn`](https://vinicunca.dev/perkakas/functions#isIncludedIn), and [`map`](https://vinicunca.dev/perkakas/functions#map) to recreate the
logic for `differenceBy`.
- Additionally, when the iteratee parameter is defined as a *property name*, use
[`prop`](https://vinicunca.dev/perkakas/functions#prop) as the iteratee *function* instead.
- Lodash accepts `null` and `undefined` values 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 to `differenceBy`, or by coalescing the input to
an empty array.
- When the `iteratee` parameter is not provided to the Lodash `differenceBy`
function (or is provided as `undefined`) it behaves like a call to
[`difference`](#difference).

#### Function iteratee

```ts
// 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

```ts
// 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

```ts
// 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

```ts
// 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

```ts
// Lodash
_.differenceBy(DATA, values);

// Convert to Lodash's `difference` and then refer to the `difference` migration
// docs
_.difference(DATA, values);
```

## differenceWith

Lodash `differenceWith` → [`differenceWith`](https://vinicunca.dev/perkakas/functions#differenceWith).

- Lodash's `differenceWith` accepts 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 `null` and `undefined` values 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 to `differenceWith`, or by coalescing the input to
an empty array.
- When the `comparator` parameter is not provided to the Lodash `differenceWith`
function (or is provided as `undefined`) it behaves like a call to
[`difference`](#difference).

#### Single exclusion array

```ts
// Lodash
_.differenceWith(DATA, values, comparator);

// Perkakas
differenceWith(DATA, values, comparator);
```

#### Multiple exclusion arrays

```ts
// Lodash
_.differenceWith(DATA, a, b, c, comparator);

// Perkakas
differenceWith(DATA, [...a, ...b, ...c], comparator);
```

#### Nullish inputs

```ts
// Lodash
_.differenceWith(DATA, values, comparator);

// Perkakas
isNonNullish(DATA) ? differenceWith(DATA, values, comparator) : [];

// Or
differenceWith(DATA ?? [], values, comparator);
```

#### Missing iteratee function

```ts
// Lodash
_.differenceWith(DATA, values);

// Convert to Lodash's `difference` and then refer to the `difference` migration
// docs
_.difference(DATA, values);
```

## divide

Lodash `divide` → [`divide`](https://vinicunca.dev/perkakas/functions#divide).

## drop

Lodash `drop` → [`drop`](https://vinicunca.dev/perkakas/functions#drop).

## dropRight

Lodash `dropRight` → [`dropLast`](https://vinicunca.dev/perkakas/functions#dropLast).

## dropRightWhile

Lodash `dropRightWhile` → [`dropLastWhile`](https://vinicunca.dev/perkakas/functions#dropLastWhile).

## dropWhile

Lodash `dropWhile` → [`dropWhile`](https://vinicunca.dev/perkakas/functions#dropWhile).

## endsWith

Lodash `endsWith` → [`endsWith`](https://vinicunca.dev/perkakas/functions#endsWith).

- Lodash supports an optional `position` parameter to check only part of the
string, while Perkakas doesn't support this parameter. To replicate this
behavior, use [`sliceString`](https://vinicunca.dev/perkakas/functions#sliceString) to 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

```ts
// Lodash
_.endsWith(input, suffix);

// Perkakas
endsWith(input, suffix);

// Native
input.endsWith(suffix);
```

#### Position parameter

```ts
// Lodash
_.endsWith(input, suffix, position);

// Perkakas
endsWith(sliceString(input, 0, position), suffix);

// Native
input.endsWith(suffix, position);
```

#### Undefined suffix

```ts
// 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`](https://www.npmjs.com/package/dompurify))
instead of basic string substitutions. In the rare case you must have an
escaping function, copy the reference code below as-is:

```ts
const ESCAPED: Readonly<Record<string, `&${string};`>> = {
  // https://github.com/lodash/lodash/blob/main/lodash.js#L399-L403
  '&': '&amp;',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  '\'': '&#39;',
};

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.escape`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/escape)
instead.
- When unavailable, `escapeRegExp` could also be replicated using [`String.prototype.replace`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
to replace matches of `/[\\^$.*+?()[\]{}|]/g` (which matches on the
characters: ^, $, ., *, +, ?, (), {}, , and |) with a backslash-escaped
version of the match (using the `"\\$&"` replacement pattern).

```ts
// 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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every) instead.

*@see "You don't need Lodash"*

```ts
// Lodash
every(DATA, predicate);

// Native
DATA.every(predicate);
```

## filter

Lodash `filter` → [`filter`](https://vinicunca.dev/perkakas/functions#filter).

## find

Lodash `find` → [`find`](https://vinicunca.dev/perkakas/functions#find).

## findIndex

Lodash `findIndex` → [`findIndex`](https://vinicunca.dev/perkakas/functions#findIndex).

## findLast

Lodash `findLast` → [`findLast`](https://vinicunca.dev/perkakas/functions#findLast).

## findLastIndex

Lodash `findLastIndex` → [`findLastIndex`](https://vinicunca.dev/perkakas/functions#findLastIndex).

## flatMap

Lodash `flatMap` → [`flatMap`](https://vinicunca.dev/perkakas/functions#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`](https://vinicunca.dev/perkakas/functions#map), and [`flat`](https://vinicunca.dev/perkakas/functions#flat) with a **large, const** depth
parameter.

```ts
// 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`](https://vinicunca.dev/perkakas/functions#map),
[`flatMap`](https://vinicunca.dev/perkakas/functions#flatMap), or [`flat`](https://vinicunca.dev/perkakas/functions#flat) (or a combination of them,
depending on the parameters used).

```ts
// Lodash
flatMapDepth(DATA);

// Perkakas
flat(DATA);
```

```ts
// Lodash
flatMapDepth(DATA, mappingFunction);

// Perkakas
flatMap(DATA, mappingFunction);
```

```ts
// 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`](https://vinicunca.dev/perkakas/functions#flat).

Either use with no params, or with a depth of **1**.

```ts
// Lodash
flatten(DATA);

// Perkakas
flat(DATA);
flat(DATA, 1 /* depth */);
```

## flattenDeep

Lodash `flattenDeep` → [`flat`](https://vinicunca.dev/perkakas/functions#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.

```ts
// Lodash
flattenDeep(DATA);

// Perkakas
flat(DATA, 100); // ✅
flat(DATA, Infinity); // ❌
```

## floor

Lodash `floor` → [`floor`](https://vinicunca.dev/perkakas/functions#floor).

## flow

Lodash `flow` → [`pipe`](https://vinicunca.dev/perkakas/functions#pipe).

## forEach

Lodash `forEach` → [`forEach`](https://vinicunca.dev/perkakas/functions#forEach).

- In Lodash `forEach` first checks if the data argument is array-like (by
looking for a `length` prop). In Perkakas the function only accepts arrays and
calling it with an object would result in a TypeScript error. Use
[`forEachObj`](https://vinicunca.dev/perkakas/functions#forEachObj) instead for those cases.
- In Lodash `forEach` stops iterating early when the callback returns `false`.
In Perkakas the callback cannot return a value (it must return `void`) and this
feature isn't available. You can build a [`pipe`](https://vinicunca.dev/perkakas/functions#pipe) that would
replicate this logic.

#### Array

```ts
// Lodash
forEach([1, 2, 3], (item) => console.log(item));

// Perkakas
forEach([1, 2, 3], (item) => {
  console.log(item);
});
```

#### Object

```ts
// 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

```ts
// 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 `for` loop.
- If the *order* of iteration doesn't matter, you can use
[`forEach`](#foreach) for arrays, or [`forEachObj`](https://vinicunca.dev/perkakas/functions#forEachObj) for
objects.
- The order in which object properties are iterated over is well-defined, but
might not be the order you expect ([MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in#description)); nevertheless, [`entries`](https://vinicunca.dev/perkakas/functions#entries) maintains the same
order, and could be used to replicate `forEachRight`.
- 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

```ts
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

```ts
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`](https://vinicunca.dev/perkakas/functions#forEachObj).

## fromPairs

Lodash `fromPairs` → [`fromEntries`](https://vinicunca.dev/perkakas/functions#fromEntries).

## get

Lodash `get` → [`prop`](https://vinicunca.dev/perkakas/functions#prop).

- In Lodash `get` takes 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., `number` when
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 function [`stringToPath`](https://vinicunca.dev/perkakas/functions#stringToPath) to convert the
path string to a path array, which could then be *spread* into `prop`.
- Lodash supports an optional last argument to `get` which defines a default
fallback in cases where the prop being accessed does not exist on the input
object. To achieve this wrap your call to `prop` with a call to
[`defaultTo`](https://vinicunca.dev/perkakas/functions#defaultTo), or use the native [Nullish coalescing operator `??`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing)
instead if the fallback is of a different type.

#### Shallow Access

```ts
const DATA = { a: 123 };

// Lodash
_.get(DATA, ['a']);

// Perkakas
prop(DATA, 'a');
```

#### Array indices

```ts
const DATA = [1, 2, 3];

// Lodash
_.get(DATA, ['0']);

// Perkakas
prop(DATA, 0);
```

#### Path Arrays

```ts
const DATA = { a: [{ b: { c: 3 } }] };

// Lodash
_.get(DATA, ['a', '0', 'b', 'c']);

// Perkakas
prop(DATA, 'a', 0, 'b', 'c');
```

#### Path Strings

```ts
const DATA = { a: [{ b: { c: 3 } }] };

// Lodash
_.get(DATA, 'a[0].b.c');

// Perkakas
prop(DATA, ...stringToPath('a[0].b.c'));
```

#### Default Fallback

```ts
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`](https://vinicunca.dev/perkakas/functions#groupBy).

- When using `groupBy` with a callback function use Perkakas's
[`groupBy`](https://vinicunca.dev/perkakas/functions#groupBy) function.
- When using `groupBy` on an array of objects and with a property name use
Perkakas's [`groupByProp`](https://vinicunca.dev/perkakas/functions#groupByProp) instead.
- When using `groupBy` on an array of primitives (e.g., `string`) and with a
property name use Perkakas's [`groupBy`](https://vinicunca.dev/perkakas/functions#groupBy) composed with
[`prop`](https://vinicunca.dev/perkakas/functions#prop).

#### Callback

```ts
const DATA = [6.1, 4.2, 6.3];

// Lodash
groupBy(DATA, Math.floor);

// Perkakas
groupBy(DATA, Math.floor);
```

#### Property Name (on objects)

```ts
const DATA = [
  { type: 'cat', name: 'felix' },
  { type: 'dog', toys: 3 },
];

// Lodash
groupBy(DATA, 'type');

// Perkakas
groupByProp(DATA, 'type');
```

#### Property Name (on primitives)

```ts
const DATA = ['one', 'two', 'three'];

// Lodash
groupBy(DATA, 'length');

// Perkakas
groupBy(DATA, prop('length'));
```

## has

Lodash `has` → [`hasProp`](https://vinicunca.dev/perkakas/functions#hasProp).

- Lodash's `has` accepts deep paths (`"a.b.c"` or `["a", "b", "c"]`). Perkakas's
`hasProp` only checks a single key.

```ts
// Lodash
has(obj, 'a');

// Perkakas
hasProp(obj, 'a');
```

## hasIn

Lodash `hasIn` → [`hasProp`](https://vinicunca.dev/perkakas/functions#hasProp).

- Lodash's `hasIn` uses [the `in` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in)
which returns `true` for **inherited** properties. Perkakas uses
[`Object.hasOwn`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn)
which ignores them.
- Lodash's `hasIn` accepts deep paths (`"a.b.c"` or `["a", "b", "c"]`).
Perkakas's `hasProp` only checks a single key.

#### Own properties

```ts
// Lodash
hasIn(obj, 'a');

// Perkakas
hasProp(obj, 'a');
```

#### Inherited properties

```ts
// Lodash
hasIn({}, 'toString'); // => true

// Perkakas
hasProp({}, 'toString'); // => false
```

## head

Lodash `head` → [`first`](https://vinicunca.dev/perkakas/functions#first).

## identity

Lodash `identity` → [`identity`](https://vinicunca.dev/perkakas/functions#identity).

In Perkakas the `identity` function is a factory that creates an *identity
function*; it needs to be called to be used.

```ts
// Lodash
map(identity);

// Perkakas
map(identity());
```

## includes

Lodash `includes` → [`isIncludedIn`](https://vinicunca.dev/perkakas/functions#isIncludedIn).

- In Lodash `includes` also works on strings and objects; In Perkakas only arrays
are supported. For objects, use [`values`](https://vinicunca.dev/perkakas/functions#values) first. For strings
prefer the native JS [`String.prototype.includes`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes).
- The 3rd optional parameter `fromIndex` isn't supported in Perkakas. You can
replicate it's usage with [`drop`](https://vinicunca.dev/perkakas/functions#drop) (for non-negative indices), or
[`takeLast`](https://vinicunca.dev/perkakas/functions#takeLast) (for non-positive indices).
- The order of the parameters is flipped in Perkakas, the item is first, and then
the array.

#### Arrays

```ts
// Lodash
includes([1, 2, 3], 1);

// Perkakas
isIncludedIn(1, [1, 2, 3]);
```

#### Objects

```ts
// Lodash
includes({ a: 1, b: 2 }, 1);

// Perkakas
isIncludedIn(1, values({ a: 1, b: 2 }));
```

#### Strings

```ts
const DATA = 'Hello, World!';

// Lodash
includes(DATA, 'lo');

// Native
DATA.includes('lo');
```

#### fromIndex: non-negative

```ts
// Lodash
includes([1, 2, 3], 1, 2);

// Perkakas
isIncludedIn(1, drop([1, 2, 3], 2));
```

#### fromIndex: non-positive

```ts
// Lodash
includes([1, 2, 3], 1, -2);

// Perkakas
isIncludedIn(1, takeLast([1, 2, 3], 2));
```

## intersection

Lodash `intersection` → [`intersection`](https://vinicunca.dev/perkakas/functions#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`](https://vinicunca.dev/perkakas/functions#unique).

#### No duplicates

```ts
// Lodash
intersection([2, 1], [2, 3]);

// Perkakas
intersection([2, 1], [2, 3]);
```

#### With duplicates

```ts
// Lodash
intersection([1, 1], [1, 1]);

// Perkakas
unique(intersection([1, 1], [1, 1]));
```

## intersectionWith

Lodash `intersectionWith` → [`intersectionWith`](https://vinicunca.dev/perkakas/functions#intersectionWith).

## invert

Lodash `invert` → [`invert`](https://vinicunca.dev/perkakas/functions#invert).

## invokeMap

Lodash `invokeMap` → [`map`](https://vinicunca.dev/perkakas/functions#map).

- `map` can be used instead of `invokeMap` by 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

```ts
// Lodash
invokeMap(
  [
    [5, 1, 7],
    [3, 2, 1],
  ],
  'sort',
);

// Perkakas
map(
  [
    [5, 1, 7],
    [3, 2, 1],
  ],
  (array) => array.sort(),
);
```

#### With arguments

```ts
// Lodash
invokeMap([123, 456], String.prototype.split, '');

// Perkakas
map([123, 456], (num) => num.toString().split(''));
```

#### Dynamic

```ts
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`](https://vinicunca.dev/perkakas/functions#isArray).

## isBoolean

Lodash `isBoolean` → [`isBoolean`](https://vinicunca.dev/perkakas/functions#isBoolean).

## isDate

Lodash `isDate` → [`isDate`](https://vinicunca.dev/perkakas/functions#isDate).

## isEmpty

Lodash `isEmpty` → [`isEmptyish`](https://vinicunca.dev/perkakas/functions#isEmptyish).

## isEqual

Lodash `isEqual` → [`isDeepEqual`](https://vinicunca.dev/perkakas/functions#isDeepEqual).

## isError

Lodash `isError` → [`isError`](https://vinicunca.dev/perkakas/functions#isError).

## isFunction

Lodash `isFunction` → [`isFunction`](https://vinicunca.dev/perkakas/functions#isFunction).

## isMatch

Lodash `isMatch` → [`hasSubObject`](https://vinicunca.dev/perkakas/functions#hasSubObject).

## isNil

Lodash `isNil` → [`isNullish`](https://vinicunca.dev/perkakas/functions#isNullish).

## isNumber

Lodash `isNumber` → [`isNumber`](https://vinicunca.dev/perkakas/functions#isNumber).

## isObjectLike

Lodash `isObjectLike` → [`isObjectType`](https://vinicunca.dev/perkakas/functions#isObjectType).

## isPlainObject

Lodash `isPlainObject` → [`isPlainObject`](https://vinicunca.dev/perkakas/functions#isPlainObject).

## isString

Lodash `isString` → [`isString`](https://vinicunca.dev/perkakas/functions#isString).

## isSymbol

Lodash `isSymbol` → [`isSymbol`](https://vinicunca.dev/perkakas/functions#isSymbol).

## join

Lodash `join` → [`join`](https://vinicunca.dev/perkakas/functions#join).

## kebabCase

Lodash `kebabCase` → [`toKebabCase`](https://vinicunca.dev/perkakas/functions#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](https://github.com/sindresorhus/type-fest/blob/main/source/words.d.ts)
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-in [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
instead.
- Lodash performs normalization on the input before splitting it, including
[`deburr`](#deburr) and 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 `kebabCase` without any input, or with an `undefined`
input, which isn't supported in Perkakas. Handle these cases before calling the
function.

#### Simple strings

```ts
// Lodash
_.kebabCase(input);

// Perkakas
toKebabCase(input);
```

#### Normalized

```ts
// 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

```ts
// Lodash
_.kebabCase();
_.kebabCase(input);

// Perkakas
('');
input !== undefined ? toKebabCase(input) : '';

// Or
toKebabCase(input ?? '');
```

## keyBy

Lodash `keyBy` → [`indexBy`](https://vinicunca.dev/perkakas/functions#indexBy).

## keys

Lodash `keys` → [`keys`](https://vinicunca.dev/perkakas/functions#keys).

## last

Lodash `last` → [`last`](https://vinicunca.dev/perkakas/functions#last).

## lowerCase

Lodash `lowerCase` → [`toTitleCase`](https://vinicunca.dev/perkakas/functions#toTitleCase).

- Use [`toLowerCase`](https://vinicunca.dev/perkakas/functions#toLowerCase) on the results of [`toTitleCase`](https://vinicunca.dev/perkakas/functions#toTitleCase),
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](https://github.com/sindresorhus/type-fest/blob/main/source/words.d.ts)
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-in [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
instead.
- Lodash performs normalization on the input before splitting it, including
[`deburr`](#deburr) and 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 `lowerCase` without any input, or with an `undefined`
input, which isn't supported in Perkakas. Handle these cases before calling the
function.

#### Basic usage

```ts
// Lodash
_.lowerCase(input);

// Perkakas
toLowerCase(toTitleCase(input));
```

#### Normalized

```ts
// 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

```ts
// Lodash
_.lowerCase();
_.lowerCase(input);

// Perkakas
('');
input !== undefined ? toLowerCase(toTitleCase(input)) : '';

// Or
toLowerCase(toTitleCase(input ?? ''));
```

## lowerFirst

Lodash `lowerFirst` → [`uncapitalize`](https://vinicunca.dev/perkakas/functions#uncapitalize).

- For display purposes, prefer using CSS; use [`::first-letter`](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter)
to target just the first letter of the word and [`text-transform: lowercase`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform#lowercase)
to lowercase it.
- Lodash allows calling `lowerFirst` without any input (or with an `undefined`
input), which results in an empty string `""`. Perkakas's `uncapitalize`
requires a string input.

#### Basic usage

```ts
// Lodash
_.lowerFirst(input);

// Perkakas
uncapitalize(input);
```

#### CSS

```tsx
// Lodash
<div>{_.lowerFirst(input)}</div>

// CSS
<style>
  .lowerFirst::first-letter {
    text-transform: lowercase;
  }
</style>
<div class="lowerFirst">{input}</div>
```

#### Missing input data

```ts
// Lodash
_.lowerFirst();
_.lowerFirst(input);

// Perkakas
input !== undefined ? uncapitalize(input) : '';

// Or
uncapitalize(input ?? '');
```

## map

Lodash `map` → [`map`](https://vinicunca.dev/perkakas/functions#map).

## mapKeys

Lodash `mapKeys` → [`mapKeys`](https://vinicunca.dev/perkakas/functions#mapKeys).

## mapValues

Lodash `mapValues` → [`mapValues`](https://vinicunca.dev/perkakas/functions#mapValues).

## max

Lodash `max` → [`firstBy`](https://vinicunca.dev/perkakas/functions#firstBy).

Use [`identity`](https://vinicunca.dev/perkakas/functions#identity) as the criteria and `"desc"` as the direction.

```ts
// Lodash
max(DATA);

// Perkakas
firstBy(DATA, [identity(), 'desc']);
```

## maxBy

Lodash `maxBy` → [`firstBy`](https://vinicunca.dev/perkakas/functions#firstBy).

Use `"desc"` as the direction.

```ts
// Lodash
maxBy(DATA, criteria);

// Perkakas
firstBy(DATA, [criteria, 'desc']);
```

## mean

Lodash `mean` → [`mean`](https://vinicunca.dev/perkakas/functions#mean).

## meanBy

Lodash `meanBy` → [`meanBy`](https://vinicunca.dev/perkakas/functions#meanBy).

## min

Lodash `min` → [`firstBy`](https://vinicunca.dev/perkakas/functions#firstBy).

Use [`identity`](https://vinicunca.dev/perkakas/functions#identity) as the criteria.

```ts
// Lodash
min(DATA);

// Perkakas
firstBy(DATA, identity());
```

## minBy

Lodash `minBy` → [`firstBy`](https://vinicunca.dev/perkakas/functions#firstBy).

## multiply

Lodash `multiply` → [`multiply`](https://vinicunca.dev/perkakas/functions#multiply).

## negate

Lodash `negate` → [`isNot`](https://vinicunca.dev/perkakas/functions#isNot).

- Unlike Lodash's `negate`, `isNot` preserves type predicates: negating a
type-guard returns a type-guard which *excludes* the guarded type, so the
result of [`filter`](https://vinicunca.dev/perkakas/functions#filter) stays narrowed.
- Lodash's `negate` forwards every argument it receives to the predicate, and
invokes it with the same `this`. `isNot` only takes a single-parameter
predicate and passes just the first argument through; wrap multi-parameter
functions with the native [logical NOT operator `!`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_NOT)
instead.
- Lodash's `negate` negates the *truthiness* of any return value, whereas
`isNot` requires a predicate that returns a `boolean`. Compose with
[`isTruthy`](https://vinicunca.dev/perkakas/functions#isTruthy) to get the Lodash semantics.

#### Predicates

```ts
// Lodash
_.negate(predicate);

// Perkakas
isNot(predicate);
```

#### Filtering

```ts
// Lodash
_.filter(DATA, _.negate(_.isString));

// Perkakas
filter(DATA, isNot(isString));
```

#### Multiple parameters

```ts
// Lodash
_.negate(isGreaterThan);

// Native
(a: number, b: number) => !isGreaterThan(a, b);
```

#### Non-boolean predicates

```ts
// Lodash
_.negate(getLength);

// Perkakas
isNot(piped(getLength, isTruthy));

// Or directly via Native JS:
(data: string) => !getLength(data);
```

## noop

Lodash `noop` → [`doNothing`](https://vinicunca.dev/perkakas/functions#doNothing).

- If a return value is needed, use [`constant`](https://vinicunca.dev/perkakas/functions#constant) with
`undefined`.
- Otherwise, use `doNothing` (which returns `void`).

```ts
// Lodash
noop;

// Perkakas
constant(undefined);
doNothing();
```

## nth

No direct Perkakas helper. Use the notes below, or native JavaScript.

*Not provided by Perkakas.*

- For index `0` use [`first`](https://vinicunca.dev/perkakas/functions#first).
- For index `-1` use [`last`](https://vinicunca.dev/perkakas/functions#last).
- For arbitrary *non-negative* indices use the native JS `data[n]`.
- Or use [`Array.prototype.at`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at) for any index.

```ts
// 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`](https://vinicunca.dev/perkakas/functions#omit).

## omitBy

Lodash `omitBy` → [`omitBy`](https://vinicunca.dev/perkakas/functions#omitBy).

## once

Lodash `once` → [`once`](https://vinicunca.dev/perkakas/functions#once).

## orderBy

Lodash `orderBy` → [`sortBy`](https://vinicunca.dev/perkakas/functions#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.

```ts
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 `pad` is used with whitespace padding (which is also the default) in order
to center text within an HTML component, prefer using CSS instead. Use
[`width`](https://developer.mozilla.org/en-US/docs/Web/CSS/width) with a
length in [`ch`](https://developer.mozilla.org/en-US/docs/Web/CSS/length#ch)
units, and [`text-align: center`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-align).
- For other uses `pad` could be replicated via [`padStart`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart)
and [`padEnd`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd),
by computing the amount of effective padding you need on one side.

#### CSS

```tsx
// Lodash
<pre>{_.pad(input, 20)}</pre>

// CSS
<div style="text-align:center;width:20ch">{input}</div>
```

#### Reference Implementation

```ts
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.padEnd`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd)
instead.
- If `padEnd` is used with whitespace padding (which is also the default) in
order to position text within an HTML component, prefer using CSS instead. Use
[`width`](https://developer.mozilla.org/en-US/docs/Web/CSS/width) with a
length in [`ch`](https://developer.mozilla.org/en-US/docs/Web/CSS/length#ch)
units, and [`text-align: left`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-align).

#### Basic Usage

```ts
// Lodash
_.padEnd(input, n);
_.padEnd(input, n, padding);

// Native
input.padEnd(n);
input.padEnd(n, padding);
```

#### CSS

```tsx
// 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.padStart`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart)
instead.
- If `padStart` is used with whitespace padding (which is also the default) in
order to position text within an HTML component, prefer using CSS instead. Use
[`width`](https://developer.mozilla.org/en-US/docs/Web/CSS/width) with a
length in [`ch`](https://developer.mozilla.org/en-US/docs/Web/CSS/length#ch)
units, and [`text-align: right`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-align).

#### Basic Usage

```ts
// Lodash
_.padStart(input, n);
_.padStart(input, n, padding);

// Native
input.padStart(n);
input.padStart(n, padding);
```

#### CSS

```tsx
// 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.parseInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseInt)
instead.
- When used as a callback, `parseInt` protects against the index parameter
unintentionally redefining the `radix`. To avoid this issue, wrap the callback
with an arrow function.

#### Basic usage

```ts
// Lodash
_.parseInt(input);
_.parseInt(input, radix);

// Native
Number.parseInt(input);
Number.parseInt(input, radix);
```

#### Callback

```ts
// Lodash
data.map(_.parseInt);

// Native
data.map((item) => Number.parseInt(item, 10)); // ✅
data.map(Number.parseInt); // ❌
```

## partial

Lodash `partial` → [`partialBind`](https://vinicunca.dev/perkakas/functions#partialBind).

The placeholder argument `_` is not supported. Some cases can be written as `partialLastBind`; otherwise, use an arrow function.

```ts
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`](https://vinicunca.dev/perkakas/functions#partialLastBind).

The placeholder argument `_` is not supported. Some cases can be written as `partialBind`; otherwise, use an arrow function.

```ts
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`](https://vinicunca.dev/perkakas/functions#partition).

## pick

Lodash `pick` → [`pick`](https://vinicunca.dev/perkakas/functions#pick).

## pickBy

Lodash `pickBy` → [`pickBy`](https://vinicunca.dev/perkakas/functions#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*](https://en.wikipedia.org/wiki/Pure_function) 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!

- `pull` is equivalent to Lodash's `difference` function. To migrate to Perkakas
first migrate calls to Lodash `_.difference` and then use the migration docs
for [`difference`](#difference) to complete the migration. **IMPORTANT**: The
Perkakas `difference` function **isn't** a drop-in replacement for the Lodash
`_.difference` function, we do **not** recommend migrating directly from
`pull` to Perkakas's `difference`.
- `pull` takes a variadic array of items to remove; `difference` take 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 `let` instead of `const`), and assign back the result
of `difference` back 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

```ts
// pull
_.pull(DATA, a, b, c, ...additional);

// difference
_.difference(DATA, [a, b, c, ...additional]);
```

#### In-place mutation

```ts
// pull
const DATA = ['a', 'b', 'c', 'd'];
_.pull(DATA, ...values);

// difference
let DATA = ['a', 'b', 'c', 'd'];
DATA = _.difference(DATA, values);
```

#### Non-mutating usage

```ts
// pull
const pulled = _.pull([...DATA], ...values);

// difference
const pulled = _.difference(DATA, values);
```

#### Nested inside an object

```ts
// 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*](https://en.wikipedia.org/wiki/Pure_function) 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!

- `pullAll` is equivalent to Lodash's `difference` function. To migrate to
Perkakas first migrate calls to Lodash `_.difference` and then use the migration
docs for [`difference`](#difference) to complete the migration. **IMPORTANT**:
The Perkakas `difference` function **isn't** a drop-in replacement for the
Lodash `_.difference` function, we do **not** recommend migrating directly
from `pullAll` to Perkakas's `difference`.
- If the mutability of the input array is desired, make sure the variable is
assignable (e.g., using `let` instead of `const`), and assign back the result
of `difference` back 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

```ts
// pullAll
const DATA = ['a', 'b', 'c', 'd'];
_.pullAll(DATA, values);

// difference
let DATA = ['a', 'b', 'c', 'd'];
DATA = _.difference(DATA, values);
```

#### Non-mutating usage

```ts
// pullAll
const pulled = _.pullAll([...DATA], values);

// difference
const pulled = _.difference(DATA, values);
```

#### Nested inside an object

```ts
// 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*](https://en.wikipedia.org/wiki/Pure_function) 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!

- `pullAllBy` is equivalent to Lodash's `differenceBy` function. To migrate to
Perkakas first migrate calls to Lodash `_.differenceBy` and then use the
migration docs for [`differenceBy`](#differenceBy) to complete the migration.
- If the mutability of the input array is desired, make sure the variable is
assignable (e.g., using `let` instead of `const`), and assign back the result
of `differenceBy` back 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

```ts
// 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

```ts
// pullAllBy
const pulled = _.pullAllBy([...DATA], values, iteratee);

// differenceBy
const pulled = _.differenceBy(DATA, values, iteratee);
```

#### Nested inside an object

```ts
// 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*](https://en.wikipedia.org/wiki/Pure_function) 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!

- `pullAllWith` is equivalent to Lodash's `differenceWith` function. To migrate
to Perkakas first migrate calls to Lodash `_.differenceWith` and then use the
migration docs for [`differenceWith`](#differenceWith) to complete the
migration. **IMPORTANT**: The Perkakas `differenceWith` function **isn't** a
drop-in replacement for the Lodash `_.differenceWith` function, we do **not**
recommend migrating directly from `pullAllWith` to Perkakas's `differenceWith`.
- If the mutability of the input array is desired, make sure the variable is
assignable (e.g., using `let` instead of `const`), and assign back the result
of `differenceWith` back 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

```ts
// 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

```ts
// pullAllWith
const pulled = _.pullAllWith([...DATA], values, comparator);

// differenceWith
const pulled = _.differenceWith(DATA, values, comparator);
```

#### Nested inside an object

```ts
// 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`](https://vinicunca.dev/perkakas/functions#randomInteger).

- When Lodash's `random` function is called with a non-integer param it returns
any number, not just integers (e.g. 2.5)! Perkakas's `randomInteger` function
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"*](https://you-dont-need.github.io/You-Dont-Need-Lodash-Underscore/#/?id=_random).
- Lodash's `random` parameters are optional. In Perkakas all parameters are
required.

#### Two integer params

```ts
// Lodash
random(1, 10);

// Perkakas
randomInteger(1, 10);
```

#### Single integer param

```ts
// Lodash
random(10);

// Perkakas
randomInteger(0, 10);
```

#### No params

```ts
// Lodash
random();

// Perkakas
randomInteger(0, 1);
```

#### Not supported: floating-point numbers

```ts
random(1.5, 3.5);
random(1.5);
random(10, true);
random(5, 10, true);
random(true);
```

## range

Lodash `range` → [`range`](https://vinicunca.dev/perkakas/functions#range).

- When calling Lodash's `range` with a single argument, an implicit `start` of
`0` is used. In Perkakas, `start` is always required.
- When `start > end`, Lodash infers a negative step (`-1`). Perkakas does not
infer the step direction — a negative `step` must be provided explicitly, or
the result is an empty array.
- Lodash special-cases a `step` of `0` by computing the length of the output as
if `step` were `1`, and filling the output with the `start` value. We throw a
`RangeError` for this case instead.
- Lodash coerces `NaN` to `0` (e.g., `_.range(NaN, 3)` → `[0, 1, 2]`). Perkakas
does not handle `NaN` inputs and their behavior is unspecified.

#### Implicit 0 start

```ts
// Lodash
_.range(5);
_.range(end);

// Perkakas
range(0, 5);
range(0, end);
```

#### With start

```ts
// Lodash
_.range(1, 5);
_.range(start, end);

// Perkakas
range(1, 5);
range(start, end);
```

#### With step

```ts
// Lodash
_.range(1, 20, 5);
_.range(start, end, step);

// Perkakas
range(1, { end: 20, step: 5 });
range(start, { end, step });
```

#### Descending range (start > end)

```ts
// Lodash
_.range(5, 1);
_.range(start, end);

// Perkakas
range(5, { end: 1, step: -1 });
range(start, { end, step: -1 });
```

#### Step of 0

```ts
// 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`](#reverse) on
[`_.range`](#range). Migrate first to that pattern, and then use the migration
guides for those functions to fully migrate to Perkakas.

```ts
// Lodash
_.rangeRight(start, end, step);
_.reverse(_.range(start, end, step));
```

## reduce

Lodash `reduce` → [`reduce`](https://vinicunca.dev/perkakas/functions#reduce).

## reduceRight

Lodash `reduceRight` → [`reduce`](https://vinicunca.dev/perkakas/functions#reduce).

Use [`reverse`](https://vinicunca.dev/perkakas/functions#reverse) on the input.

```ts
// 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`](https://vinicunca.dev/perkakas/functions#filter).

Wrap the callback with [`isNot`](https://vinicunca.dev/perkakas/functions#isNot).

```ts
// 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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat)
instead.

```ts
// 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.replace`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
instead.
- Lodash typing incorrectly allows calling `replace` with two arguments. These
calls return strings as-is, while RegExp inputs are converted to strings.

#### Regular Usage

```ts
// Lodash
_.replace(input, pattern, replacement);

// Native
input.replace(pattern, replacement);
```

#### Invalid 2-parameter usage

```ts
// Lodash
_.replace(input, replacement);
_.replace(/pattern/, replacement);

// Native
input; // Returns input unchanged
String(/pattern/); // "/pattern/" - RegExp converted to string
```

## reverse

Lodash `reverse` → [`reverse`](https://vinicunca.dev/perkakas/functions#reverse).

Lodash mutates the input but in Perkakas a new array is returned instead.

```ts
// Lodash
const DATA = [1, 2, 3];
reverse(DATA);

// Perkakas
let DATA = [1, 2, 3];
DATA = reverse(DATA);
```

## round

Lodash `round` → [`round`](https://vinicunca.dev/perkakas/functions#round).

## sample

Lodash `sample` → [`sample`](https://vinicunca.dev/perkakas/functions#sample).

- The `sample` function 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`](https://vinicunca.dev/perkakas/functions#only).
- For objects, first call [`values`](https://vinicunca.dev/perkakas/functions#values).
- If you are using `sample` in an extremely hot path where you need the most
efficient implementation possible, prefer a native implementation instead.

#### Simple

```ts
// 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

```ts
// Lodash
sample(DATA);

// Native
DATA[Math.floor(Math.random() * DATA.length)]!;
DATA.at(Math.floor(Math.random() * DATA.length))!;
```

## sampleSize

Lodash `sampleSize` → [`sample`](https://vinicunca.dev/perkakas/functions#sample).

- Unlike Lodash, In Perkakas the `sample` function returns items in the **same
order** they appear in the input. Use the `shuffle` function if you also need
to randomize their order.
- The second parameter to `sampleSize` in Lodash is optional, and defaults to
**1** when not provided. In Perkakas this is a required param.

```ts
// Lodash
sampleSize(DATA);

// Perkakas
sample(DATA, 1);
```

```ts
// 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`](https://vinicunca.dev/perkakas/functions#setPath).

- In Lodash the `set` function 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 function
[`stringToPath`](https://vinicunca.dev/perkakas/functions#stringToPath) to translate string paths to array paths.
- Unlike the Lodash `set` function, 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 [`set`](https://vinicunca.dev/perkakas/functions#set) to update an existing prop in an
object (best type-safety and with IDE type-ahead), and
[`addProp`](https://vinicunca.dev/perkakas/functions#addProp) to add a new prop to the object (or to override
its *type*)
- In Lodash `set` *mutates* the input object. In Perkakas a **new** object is
returned instead. The input object is never mutated.

```ts
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`](https://vinicunca.dev/perkakas/functions#shuffle).

## size

Lodash `size` → [`length`](https://vinicunca.dev/perkakas/functions#length).

## snakeCase

Lodash `snakeCase` → [`toSnakeCase`](https://vinicunca.dev/perkakas/functions#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](https://github.com/sindresorhus/type-fest/blob/main/source/words.d.ts)
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-in [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
instead.
- Lodash performs normalization on the input before splitting it, including
[`deburr`](#deburr) and 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 `snakeCase` without any input, or with an `undefined`
input, which isn't supported in Perkakas. Handle these cases before calling the
function.

#### Simple strings

```ts
// Lodash
_.snakeCase(input);

// Perkakas
toSnakeCase(input);
```

#### Normalized

```ts
// 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

```ts
// 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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) instead.

*@see "You don't need Lodash"*

```ts
// Lodash
some(DATA, predicate);

// Native
DATA.some(predicate);
```

## sortBy

Lodash `sortBy` → [`sortBy`](https://vinicunca.dev/perkakas/functions#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.

```ts
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`](https://vinicunca.dev/perkakas/functions#sortedIndex).

## sortedIndexBy

Lodash `sortedIndexBy` → [`sortedIndexBy`](https://vinicunca.dev/perkakas/functions#sortedIndexBy).

## sortedLastIndex

Lodash `sortedLastIndex` → [`sortedLastIndex`](https://vinicunca.dev/perkakas/functions#sortedLastIndex).

## sortedLastIndexBy

Lodash `sortedLastIndexBy` → [`sortedLastIndexBy`](https://vinicunca.dev/perkakas/functions#sortedLastIndexBy).

## sortedUniq

Lodash `sortedUniq` → [`unique`](https://vinicunca.dev/perkakas/functions#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`](https://vinicunca.dev/perkakas/functions#filter) with the
following predicate:

```ts
// Lodash
sortedUniq(DATA);

// Perkakas
unique(DATA);
filter(DATA, (item, index, array) => index === 0 || item !== array[index - 1]);
```

## split

Lodash `split` → [`split`](https://vinicunca.dev/perkakas/functions#split).

- Lodash allows calling `split` without a `separator` parameter (or setting it
to `undefined`) which returns the input string as a single-element array (the
same happens when `split` is used as a callback). Perkakas needs the input
wrapped in an array manually.
- Lodash accepts `null` and `undefined` input values, converting them to empty
strings. Perkakas needs these values handled separately.
- When the `separator` parameter is `""`, Lodash uses complex logic to parse
compound Unicode characters (like family emojis 👨‍👩‍👧‍👦 or flags with modifiers 🏳️‍🌈)
as single graphemes. Perkakas uses native [`String.prototype.split`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split)
which splits these into component parts. Use [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
with [`granularity: "grapheme"`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/Segmenter#grapheme)
for proper Unicode handling. This is rarely needed - simple Unicode characters
(like non-Latin alphabets) work fine.

#### Basic usage

```ts
// Lodash
_.split(input, separator);
_.split(input, separator, limit);

// Perkakas
split(input, separator);
split(input, separator, limit);
```

#### Without separator

```ts
// Lodash
_.split(input);
_.map(data, _.split);

// Native
[input];
map(data, (item) => [item]);
```

#### Nullish inputs

```ts
// 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

```ts
// 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`](https://vinicunca.dev/perkakas/functions#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](https://github.com/sindresorhus/type-fest/blob/main/source/words.d.ts)
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-in [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
instead.
- 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
[`deburr`](#deburr) and 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 `startCase` without any input, or with an `undefined`
input, which isn't supported in Perkakas. Handle these cases before calling the
function.

#### Simple strings

```ts
// Lodash
_.startCase(input);

// Perkakas
toTitleCase(input, { preserveConsecutiveUppercase: false });
```

#### Normalized

```ts
// 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

```ts
// Lodash
_.startCase();
_.startCase(input);

// Perkakas
('');
input !== undefined
  ? toTitleCase(input, { preserveConsecutiveUppercase: false })
  : '';

// Or
toTitleCase(input ?? '', { preserveConsecutiveUppercase: false });
```

## startsWith

Lodash `startsWith` → [`startsWith`](https://vinicunca.dev/perkakas/functions#startsWith).

- Lodash supports an optional `position` parameter to check from a specific
index, while Perkakas only checks from the beginning of the string. Compose
[`sliceString`](https://vinicunca.dev/perkakas/functions#sliceString) with `startsWith` to replicate this
behavior.
- Lodash allows calling the function without a `target` parameter (or using
`undefined` for it). These would result in the function always returning
`true`. In Perkakas the `target` is required and handling `undefined` should be
done before calling `startsWith`.
- Lodash also allows calling the function without an `input` parameter (or using
`undefined` for it). These would result in the function always returning
`false` (unless `target` is *also* `""` or `undefined`). This parameter is
also required in Perkakas; handle the `undefined` explicitly.

#### Basic usage

```ts
// Lodash
_.startsWith(input, target);

// Perkakas
startsWith(input, target);
```

#### Position parameter

```ts
// Lodash
_.startsWith(input, target, position);

// Perkakas
startsWith(sliceString(input, position), target);
```

#### Missing parameters

```ts
// Lodash
_.startsWith();
_.startsWith(input);

// Native
true;
true;
```

#### Undefined target

```ts
// Lodash
_.startsWith(input, target);

// Perkakas
target === undefined || startsWith(input, target);

// Or
startsWith(input, target ?? '');
```

#### Undefined input

```ts
// 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`](https://vinicunca.dev/perkakas/functions#subtract).

## sum

Lodash `sum` → [`sum`](https://vinicunca.dev/perkakas/functions#sum).

## sumBy

Lodash `sumBy` → [`sumBy`](https://vinicunca.dev/perkakas/functions#sumBy).

## take

Lodash `take` → [`take`](https://vinicunca.dev/perkakas/functions#take).

## takeRight

Lodash `takeRight` → [`takeLast`](https://vinicunca.dev/perkakas/functions#takeLast).

## takeRightWhile

Lodash `takeRightWhile` → [`takeLastWhile`](https://vinicunca.dev/perkakas/functions#takeLastWhile).

## takeWhile

Lodash `takeWhile` → [`takeWhile`](https://vinicunca.dev/perkakas/functions#takeWhile).

## tap

Lodash `tap` → [`tap`](https://vinicunca.dev/perkakas/functions#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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/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](https://handlebarsjs.com/)).

#### Template Literals

```ts
// Lodash
const greet = _.template('Hello <%= firstName %> <%= lastName %>!');

// Template literals
function greet({
  firstName,
  lastName,
}: {
  firstName: string;
  lastName: string;
}) {
  return `Hello ${firstName} ${lastName}!`;
}
```

#### HTML

```tsx
// 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

```tsx
// 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`](https://vinicunca.dev/perkakas/functions#funnel).

- `throttle` can be implemented using the `funnel` utility. A reference
implementation is provided below, and a more expanded version with inline
documentation and tests is available in the test file [`funnel.lodash-throttle.test.ts`](https://github.com/remeda/remeda/blob/main/packages/remeda/src/funnel.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`](https://github.com/remeda/remeda/blob/main/packages/remeda/src/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 START` and `REFERENCE END`
markers.

#### Reference

```ts
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

```ts
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

```ts
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`](https://vinicunca.dev/perkakas/functions#times).

## toLower

Lodash `toLower` → [`toLowerCase`](https://vinicunca.dev/perkakas/functions#toLowerCase).

- For display purposes, prefer using CSS [`text-transform: lowercase`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform#lowercase).
- Lodash allows calling `toLower` without any input, or with an `undefined`
input, which isn't supported in Perkakas. Handle these cases before calling the
function.

#### Basic usage

```ts
// Lodash
_.toLower(input);

// Perkakas
toLowerCase(input);
```

#### CSS

```tsx
// Lodash
<div>{_.toLower(input)}</div>

// CSS
<div style="text-transform:lowercase">{input}</div>
```

#### Missing input

```ts
// Lodash
_.toLower();
_.toLower(input);

// Perkakas
('');
input !== undefined ? toLowerCase(input) : '';

// Or
toLowerCase(input ?? '');
```

## toPairs

Lodash `toPairs` → [`entries`](https://vinicunca.dev/perkakas/functions#entries).

## toPath

Lodash `toPath` → [`stringToPath`](https://vinicunca.dev/perkakas/functions#stringToPath).

## toUpper

Lodash `toUpper` → [`toUpperCase`](https://vinicunca.dev/perkakas/functions#toUpperCase).

- For display purposes, prefer using CSS [`text-transform: uppercase`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform#uppercase).
- Lodash allows calling `toUpper` without any input, or with an `undefined`
input, which isn't supported in Perkakas. Handle these cases before calling the
function.

#### Basic usage

```ts
// Lodash
_.toUpper(input);

// Perkakas
toUpperCase(input);
```

#### CSS

```tsx
// Lodash
<div>{_.toUpper(input)}</div>

// CSS
<div style="text-transform:uppercase">{input}</div>
```

#### Missing input

```ts
// Lodash
_.toUpper();
_.toUpper(input);

// Perkakas
('');
input !== undefined ? toUpperCase(input) : '';

// Or
toUpperCase(input ?? '');
```

## transform

Lodash `transform` → [`reduce`](https://vinicunca.dev/perkakas/functions#reduce).

## trim

No direct Perkakas helper. Use the notes below, or native JavaScript.

*Not provided by Perkakas.*

- When the second `characters` parameter is not provided to Lodash (or when it
is `undefined`), all whitespace characters would be trimmed. This is the same
as the native [`String.prototype.trim`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim).
- The native `trim` doesn't support the additional `characters` parameter that
allows changing the trimmed characters. Instead, create a regex that would
match `characters` anchored to either the start or the end of the string
(`^[${characters}]+|[${characters}]+$`) and then use [`String.prototype.replace`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
to replace them with the empty string (`""`) and use the [`g`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global)
RegExp flag so that it matches both the start and the end sequences. You might
need to use [`RegExp.escape`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/escape)
on `characters` if they might contain special regex characters.
- Lodash does complex grapheme parsing, but this is usually not needed unless
the `characters` parameter itself contains complex Unicode graphemes (like
family emojis 👨‍👩‍👧‍👦 or flags with modifiers 🏳️‍🌈 that you want to trim). In these
cases use [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
with [`granularity: "grapheme"`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/Segmenter#granularity)
to split the string, then use Perkakas's [`dropWhile`](https://vinicunca.dev/perkakas/functions#dropWhile) (for
left trimming) and [`dropLastWhile`](https://vinicunca.dev/perkakas/functions#dropLastWhile) (for right
trimming), and rejoin the array back with [`join`](https://vinicunca.dev/perkakas/functions#join).
- Lodash allows calling `trim` without any input (or with an `undefined` input),
which results in an empty string `""`. This requires explicit handling in
replacements.

#### Whitespaces

```ts
// Lodash
_.trim(input);

// Native
input.trim();
```

#### Callback

```ts
// Lodash
_.map(data, _.trim);

// Native
data.map(String.prototype.trim);
```

#### Characters

```ts
// Lodash
_.trim(input, characters);

// Native
const escaped = RegExp.escape(characters);
input.replace(new RegExp(`^[${escaped}]+|[${escaped}]+$`, 'g'), '');
```

#### Graphemes

```ts
// 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

```ts
// 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 `characters` parameter is not provided to Lodash (or when it
is `undefined`), all whitespace characters would be trimmed. This is the same
as the native [`String.prototype.trimEnd`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd).
- The native `trimEnd` doesn't support the additional `characters` parameter
that allows changing the trimmed characters. Instead, create a regex that
would match `characters` anchored to the end of the string
(`[${characters}]+$`) and then use [`String.prototype.replace`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
to replace them with the empty string (`""`). You might need to use [`RegExp.escape`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/escape)
on `characters` if they might contain special regex characters.
- Lodash does complex grapheme parsing, but this is usually not needed unless
the `characters` parameter itself contains complex Unicode graphemes (like
family emojis 👨‍👩‍👧‍👦 or flags with modifiers 🏳️‍🌈 that you want to trim). In these
cases use [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
with [`granularity: "grapheme"`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/Segmenter#granularity)
to split the string, then use Perkakas's [`dropLastWhile`](https://vinicunca.dev/perkakas/functions#dropLastWhile),
and rejoin the array back with [`join`](https://vinicunca.dev/perkakas/functions#join).
- Lodash allows calling `trimEnd` without any input (or with an `undefined`
input), which results in an empty string `""`. This requires explicit handling
in replacements.

#### Whitespaces

```ts
// Lodash
_.trimEnd(input);

// Native
input.trimEnd();
```

#### Callback

```ts
// Lodash
_.map(data, _.trimEnd);

// Native
data.map(String.prototype.trimEnd);
```

#### Characters

```ts
// Lodash
_.trimEnd(input, characters);

// Native
input.replace(new RegExp(`[${RegExp.escape(characters)}]+$`), '');
```

#### Graphemes

```ts
// 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

```ts
// 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 `characters` parameter is not provided to Lodash (or when it
is `undefined`), all whitespace characters would be trimmed. This is the same
as the native [`String.prototype.trimStart`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart).
- The native `trimStart` doesn't support the additional `characters` parameter
that allows changing the trimmed characters. Instead, create a regex that
would match `characters` anchored to the start of the string
(`^[${characters}]+`) and then use [`String.prototype.replace`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
to replace them with the empty string (`""`). You might need to use [`RegExp.escape`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/escape)
on `characters` if they might contain special regex characters.
- Lodash does complex grapheme parsing, but this is usually not needed unless
the `characters` parameter itself contains complex Unicode graphemes (like
family emojis 👨‍👩‍👧‍👦 or flags with modifiers 🏳️‍🌈 that you want to trim). In these
cases use [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
with [`granularity: "grapheme"`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/Segmenter#granularity)
to split the string, then use Perkakas's [`dropWhile`](https://vinicunca.dev/perkakas/functions#dropWhile), and
then rejoin the array back with [`join`](https://vinicunca.dev/perkakas/functions#join).
- Lodash allows calling `trimStart` without any input (or with an `undefined`
input), which results in an empty string `""`. This requires explicit handling
in replacements.

#### Whitespaces

```ts
// Lodash
_.trimStart(input);

// Native
input.trimStart();
```

#### Callback

```ts
// Lodash
_.map(data, _.trimStart);

// Native
data.map(String.prototype.trimStart);
```

#### Characters

```ts
// Lodash
_.trimStart(input, characters);

// Native
input.replace(new RegExp(`^[${RegExp.escape(characters)}]+`), '');
```

#### Graphemes

```ts
// 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

```ts
// Lodash
_.trimStart();
_.trimStart(input);

// Native
('');
input?.trimStart() ?? '';
```

## truncate

Lodash `truncate` → [`truncate`](https://vinicunca.dev/perkakas/functions#truncate).

- Perkakas requires an explicit `length` parameter, while Lodash defaults to
**30** when it isn't provided.
- Lodash computes the length of the `input` and `omission` strings 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 the `omission` itself when it's too long. Lodash will never
truncate the `omission` and therefore might return an output that exceeds
`length`.

#### Default length

```ts
// Lodash
_.truncate(input);

// Perkakas
truncate(input, 30);
```

#### Custom length

```ts
// Lodash
_.truncate(input, { length });

// Perkakas
truncate(input, length);
```

#### With options

```ts
// 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:

```ts
const UNESCAPED: Readonly<Record<`&${string};`, string>> = {
  // https://github.com/lodash/lodash/blob/main/lodash.js#L408-L412
  '&amp;': '&',
  '&lt;': '<',
  '&gt;': '>',
  '&quot;': '"',
  '&#39;': '\'',
};

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`](https://vinicunca.dev/perkakas/functions#unique).

## uniqBy

Lodash `uniqBy` → [`uniqueBy`](https://vinicunca.dev/perkakas/functions#uniqueBy).

## uniqWith

Lodash `uniqWith` → [`uniqueWith`](https://vinicunca.dev/perkakas/functions#uniqueWith).

## upperCase

Lodash `upperCase` → [`toTitleCase`](https://vinicunca.dev/perkakas/functions#toTitleCase).

- Use [`toUpperCase`](https://vinicunca.dev/perkakas/functions#toUpperCase) on the results of [`toTitleCase`](https://vinicunca.dev/perkakas/functions#toTitleCase),
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](https://github.com/sindresorhus/type-fest/blob/main/source/words.d.ts)
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-in [`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
instead.
- Lodash performs normalization on the input before splitting it, including
[`deburr`](#deburr) and 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 `upperCase` without any input, or with an `undefined`
input, which isn't supported in Perkakas. Handle these cases before calling the
function.

#### Basic usage

```ts
// Lodash
_.upperCase(input);

// Perkakas
toUpperCase(toTitleCase(input));
```

#### Normalized

```ts
// 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

```ts
// Lodash
_.upperCase();
_.upperCase(input);

// Perkakas
('');
input !== undefined ? toUpperCase(toTitleCase(input)) : '';

// Or
toUpperCase(toTitleCase(input ?? ''));
```

## upperFirst

Lodash `upperFirst` → [`capitalize`](https://vinicunca.dev/perkakas/functions#capitalize).

- For display purposes, prefer using CSS; use [`::first-letter`](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter)
to target just the first letter of the word and [`text-transform: uppercase`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform#uppercase)
to capitalize it.
- Lodash allows calling `upperFirst` without any input (or with an `undefined`
input), which results in an empty string `""`. Perkakas's `capitalize` requires
a string input.

#### Basic usage

```ts
// Lodash
_.upperFirst(input);

// Perkakas
capitalize(input);
```

#### CSS

```tsx
// Lodash
<div>{_.upperFirst(input)}</div>

// CSS
<style>
  .upperFirst::first-letter {
    text-transform: uppercase;
  }
</style>
<div class="upperFirst">{input}</div>
```

#### Missing input data

```ts
// Lodash
_.upperFirst();
_.upperFirst(input);

// Perkakas
input !== undefined ? capitalize(input) : '';

// Or
capitalize(input ?? '');
```

## values

Lodash `values` → [`values`](https://vinicunca.dev/perkakas/functions#values).

## without

No direct Perkakas helper. Use the notes below, or native JavaScript.

*Not provided by Perkakas.*

- `without` is equivalent to Lodash's `difference` function. To migrate to
Perkakas first migrate calls to Lodash `_.difference` and then use the migration
docs for [`difference`](https://vinicunca.dev/#difference) to complete the migration.
**IMPORTANT**: The Perkakas `difference` function **isn't** a drop-in
replacement for the Lodash `_.difference` function, we do **not** recommend
migrating directly from `without` to Perkakas's `difference`.
- `without` takes variadic arguments; `difference` takes an explicit array.
You will need to wrap your items in an array when migrating.

```ts
// 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.*

- `words` is often used to convert between different cases of identifiers and
keys. Use [`toCamelCase`](https://vinicunca.dev/perkakas/functions#toCamelCase), [`toKebabCase`](https://vinicunca.dev/perkakas/functions#toKebabCase),
[`toSnakeCase`](https://vinicunca.dev/perkakas/functions#toSnakeCase), or [`toTitleCase`](https://vinicunca.dev/perkakas/functions#toTitleCase)
instead.
- If `words` is used for simple splitting tasks, it can often be replaced with
[`String.prototype.split`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split)
using 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.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)
with [`granularity: "word"`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/Segmenter#word).
- When provided with the optional `pattern` parameter, `words` defers the call
to [`String.prototype.match`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match)
as-is.

#### Case conversion

```ts
// 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

```ts
// 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

```ts
// Lodash
_.words(input);

// Native
const segmenter = new Intl.Segmenter('en', { granularity: 'word' });
Array.from(segmenter.segment(input)).map(({ segment }) => segment);
```

#### Custom pattern

```ts
// Lodash
_.words(input, pattern);

// Native
input.match(pattern);
```

## zip

Lodash `zip` → [`zip`](https://vinicunca.dev/perkakas/functions#zip).

## zipObject

No direct Perkakas helper. Use the notes below, or native JavaScript.

*Not provided by Perkakas.*

Compose [`zip`](https://vinicunca.dev/perkakas/functions#zip) and [`fromEntries`](https://vinicunca.dev/perkakas/functions#fromEntries):

```ts
// Lodash
zipObject(keys, values);

// Perkakas
fromEntries(zip(keys, values));
```

## zipWith

Lodash `zipWith` → [`zipWith`](https://vinicunca.dev/perkakas/functions#zipWith).


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
