nc.arr
Array helpers. None of them mutate what you pass in, and item types carry through (chunk(numbers, 2) is a number[][]). Many take an iteratee: a property name, which your editor completes, or a function.
const { arr } = require("@ix-xs/node-comfort");
import { chunk } from "@ix-xs/node-comfort/arr";arr.chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
arr.groupBy(users, "role"); // { admin: [...], user: [...] }
arr.sortBy(users, ["lastName", "age"]);nc.arr.Functions
nc.arr.chunk()
chunk<T>(array: readonly T[], size: number): T[][]Splits an array into groups of size items. The last group can be shorter.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
size | number |
Returns T[][]
Example
arr.chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
arr.chunk(emails, 100).forEach(sendBatch);nc.arr.windows()
windows<T>(array: readonly T[], size: number, step?: number): T[][]Sliding windows of size items, moving by step. Good for moving averages and comparing neighbours.
Parameters
| Name | Type | Description |
|---|---|---|
array | readonly T[] | |
size | number | |
stepoptional | number | Default: 1 |
Returns T[][]
Example
arr.windows([1, 2, 3, 4], 2); // [[1, 2], [2, 3], [3, 4]]
arr.windows([1, 2, 3, 4, 5], 3, 2); // [[1, 2, 3], [3, 4, 5]]nc.arr.first()2 overloads
first<T>(array: readonly T[]): T | undefined
first<T>(array: readonly T[], n: number): T[]The first item, or the first n items.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
n | number |
Returns T[]
Example
arr.first([1, 2, 3]); // 1
arr.first([1, 2, 3], 2); // [1, 2]nc.arr.last()2 overloads
last<T>(array: readonly T[]): T | undefined
last<T>(array: readonly T[], n: number): T[]The last item, or the last n items.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
n | number |
Returns T[]
Example
arr.last([1, 2, 3]); // 3
arr.last([1, 2, 3], 2); // [2, 3]nc.arr.paginate()
paginate<T>(array: readonly T[], page?: number, perPage?: number): Page<T>One page of an array, with what you need to draw pagination controls. Pages start at 1; an out-of-range page is brought back into range.
Parameters
| Name | Type | Description |
|---|---|---|
array | readonly T[] | |
pageoptional | number | Default: 1 |
perPageoptional | number | Default: 20 |
Returns Page<T>
Example
arr.paginate(products, 2, 20);
// { items: [...], page: 2, perPage: 20, total: 95, pages: 5, hasPrev: true, hasNext: true }nc.arr.unique()
unique<T>(array: readonly T[], iteratee?: Iteratee<T>): T[]Removes duplicates. With an iteratee, items that give the same value are duplicates; the first one is kept.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
iterateeoptional | Iteratee<T> |
Returns T[]
Example
arr.unique([1, 1, 2, 3]); // [1, 2, 3]
arr.unique(users, "email"); // one user per email
arr.unique(tags, (t) => t.toLowerCase());nc.arr.groupBy()
groupBy<T>(array: readonly T[], iteratee: Iteratee<T>): Record<string, T[]>Groups items by key.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
iteratee | Iteratee<T> |
Returns Record<string, T[]>
Example
arr.groupBy(users, "role"); // { admin: [...], user: [...] }
arr.groupBy([1, 2, 3, 4], (n) => (n % 2 ? "odd" : "even")); // { odd: [1, 3], even: [2, 4] }nc.arr.keyBy()
keyBy<T>(array: readonly T[], iteratee: Iteratee<T>): Record<string, T>Indexes items by key. If two items share a key, the last one wins.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
iteratee | Iteratee<T> |
Returns Record<string, T>
Example
const byId = arr.keyBy(users, "id");
byId[42]; // user 42nc.arr.toMap()
toMap<T, V = T>(array: readonly T[], key: Iteratee<T>, value?: ((item: T, index: number) => V) | undefined): Map<any, V>Builds a Map from an array. Unlike keyBy(), keys keep their type.
Parameters
| Name | Type | Description |
|---|---|---|
array | readonly T[] | |
key | Iteratee<T> | |
valueoptional | (item: T, index: number) => V | The value to store. Defaults to the item. |
Returns Map<any, V>
Example
arr.toMap(users, "id"); // Map<number, User>
arr.toMap(users, "id", (u) => u.name); // Map<number, string>nc.arr.countBy()
countBy<T>(array: readonly T[], iteratee?: Iteratee<T>): Record<string, number>Counts items per key.
Parameters
| Name | Type | Description |
|---|---|---|
array | readonly T[] | |
iterateeoptional | Iteratee<T> | Defaults to the item itself. |
Returns Record<string, number>
Example
arr.countBy(["a", "b", "a"]); // { a: 2, b: 1 }
arr.countBy(users, "country"); // { FR: 12, US: 7 }nc.arr.count()
count<T>(array: readonly T[], predicate: (item: T, index: number) => unknown): numberCounts the items that pass a test.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
predicate | (item: T, index: number) => unknown |
Returns number
Example
arr.count(users, (u) => u.active);nc.arr.partition()
partition<T>(array: readonly T[], predicate: (item: T, index: number) => unknown): [T[], T[]]Splits items into those that pass a test and those that don't.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
predicate | (item: T, index: number) => unknown |
Returns [T[], T[]]
Example
const [adults, minors] = arr.partition(people, (p) => p.age >= 18);nc.arr.pluck()
pluck<T, K extends keyof T>(array: readonly T[], key: K): T[K][]Takes one property from every item.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
key | K |
Returns Array<T[K]>
Example
arr.pluck(users, "email"); // ["ada@x.io", "bob@x.io"]nc.arr.sortBy()
sortBy<T>(array: readonly T[], keys: SortKey<T> | SortKey<T>[], order?: "asc" | "desc" | SortByOptions): T[]Sorts by one or more keys, each with its own direction if you like. The sort is stable and null/undefined always go last.
Parameters
| Name | Type | Description |
|---|---|---|
array | readonly T[] | |
keys | SortKey<T> | Array<SortKey<T>> | |
orderoptional | "asc" | "desc" | SortByOptions | Default: "asc" |
Returns T[]
Example
arr.sortBy(users, "age"); // youngest first
arr.sortBy(users, "age", "desc"); // oldest first
arr.sortBy(users, [["age", "desc"], "lastName"]);
arr.sortBy(files, "name", { natural: true }); // "file2" before "file10"nc.arr.shuffle()
shuffle<T>(array: readonly T[]): T[]A shuffled copy (Fisher-Yates).
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
Returns T[]
nc.arr.sample()
sample<T>(array: readonly T[]): T | undefinedOne random item, or undefined if the array is empty.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
Returns T | undefined
nc.arr.sampleSize()
sampleSize<T>(array: readonly T[], n: number): T[]n random items, never the same one twice.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
n | number |
Returns T[]
Example
arr.sampleSize(players, 3);nc.arr.rotate()
rotate<T>(array: readonly T[], n?: number): T[]Rotates items. Positive n moves items from the start to the end, negative n the other way.
Parameters
| Name | Type | Description |
|---|---|---|
array | readonly T[] | |
noptional | number | Default: 1 |
Returns T[]
Example
arr.rotate([1, 2, 3, 4], 1); // [2, 3, 4, 1]
arr.rotate([1, 2, 3, 4], -1); // [4, 1, 2, 3]nc.arr.move()
move<T>(array: readonly T[], from: number, to: number): T[]Moves one item to another position. Negative indexes count from the end.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
from | number |
to | number |
Returns T[]
Example
arr.move(["a", "b", "c"], 0, 2); // ["b", "c", "a"]nc.arr.swap()
swap<T>(array: readonly T[], i: number, j: number): T[]Swaps two items.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
i | number |
j | number |
Returns T[]
Example
arr.swap(["a", "b", "c"], 0, 2); // ["c", "b", "a"]nc.arr.difference()
difference<T>(array: readonly T[], ...others: (readonly T[])[]): T[]Items of array that appear in none of the others.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
...others | ...ReadonlyArray<T> |
Returns T[]
Example
arr.difference([1, 2, 3, 4], [2, 4]); // [1, 3]nc.arr.intersection()
intersection<T>(...arrays: (readonly T[])[]): T[]Items present in every array, without duplicates.
Parameters
| Name | Type |
|---|---|
...arrays | ...ReadonlyArray<T> |
Returns T[]
Example
arr.intersection([1, 2, 3], [2, 3, 4], [3, 2]); // [2, 3]nc.arr.union()
union<T>(...arrays: (readonly T[])[]): T[]Items present in any array, without duplicates.
Parameters
| Name | Type |
|---|---|
...arrays | ...ReadonlyArray<T> |
Returns T[]
Example
arr.union([1, 2], [2, 3], [3, 4]); // [1, 2, 3, 4]nc.arr.without()
without<T>(array: readonly T[], ...values: T[]): T[]A copy without the given values. NaN works too.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
...values | ...T |
Returns T[]
Example
arr.without([1, 2, 3, 2], 2); // [1, 3]nc.arr.remove()
remove<T>(array: readonly T[], predicate: (item: T, index: number) => unknown): T[]A copy without the items that pass the test.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
predicate | (item: T, index: number) => unknown |
Returns T[]
Example
arr.remove([1, 2, 3, 4], (n) => n % 2 === 0); // [1, 3]nc.arr.compact()
compact<T>(array: readonly T[]): Exclude<T, false | "" | 0 | 0n | null | undefined>[]Removes falsy values (false, null, undefined, 0, "", NaN).
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
Returns Array<Exclude<T, false | 0 | 0n | "" | null | undefined>>
Example
arr.compact([0, 1, false, 2, "", 3, null]); // [1, 2, 3]nc.arr.toggle()
toggle<T>(array: readonly T[], item: T): T[]Adds the item if it's missing, removes it if it's there. Handy for multi-select state.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
item | T |
Returns T[]
Example
arr.toggle(["a", "b"], "b"); // ["a"]
arr.toggle(["a"], "b"); // ["a", "b"]nc.arr.upsert()
upsert<T>(array: readonly T[], item: T, identity: Iteratee<T>): T[]Replaces the item with the same identity, or appends it if there's none.
Parameters
| Name | Type | Description |
|---|---|---|
array | readonly T[] | |
item | T | |
identity | Iteratee<T> | What makes two items "the same", like "id". |
Returns T[]
Example
arr.upsert(users, { id: 2, name: "Bob" }, "id");nc.arr.insert()
insert<T>(array: readonly T[], index: number, ...items: T[]): T[]Inserts items at an index. Negative indexes count from the end.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
index | number |
...items | ...T |
Returns T[]
Example
arr.insert(["a", "d"], 1, "b", "c"); // ["a", "b", "c", "d"]nc.arr.flatten()
flatten<T, D extends number = 1>(array: readonly T[], depth?: D): FlatArray<T[], D>[]Flattens nested arrays, one level by default.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
depthoptional | D |
Returns FlatArray<T[], D>[]
Example
arr.flatten([1, [2, [3, [4]]]]); // [1, 2, [3, [4]]]
arr.flatten([1, [2, [3, [4]]]], Infinity); // [1, 2, 3, 4]nc.arr.zip()
zip<T>(...arrays: (readonly T[])[]): (T | undefined)[][]Pairs items by position. Shorter arrays leave undefined holes.
Parameters
| Name | Type |
|---|---|
...arrays | ...ReadonlyArray<T> |
Returns Array<Array<T | undefined>>
Example
arr.zip(["a", "b"], [1, 2]); // [["a", 1], ["b", 2]]nc.arr.unzip()
unzip<T>(tuples: readonly (readonly T[])[]): (T | undefined)[][]The opposite of zip(): rows become columns.
Parameters
| Name | Type |
|---|---|
tuples | ReadonlyArray<ReadonlyArray<T>> |
Returns Array<Array<T | undefined>>
Example
arr.unzip([["a", 1], ["b", 2]]); // [["a", "b"], [1, 2]]nc.arr.interleave()
interleave<T>(...arrays: (readonly T[])[]): T[]Takes items from each array in turn, then appends the rest.
Parameters
| Name | Type |
|---|---|
...arrays | ...ReadonlyArray<T> |
Returns T[]
Example
arr.interleave([1, 3, 5], [2, 4]); // [1, 2, 3, 4, 5]nc.arr.cartesian()
cartesian<T>(...arrays: (readonly T[])[]): T[][]Every combination of one item from each array.
Parameters
| Name | Type |
|---|---|
...arrays | ...ReadonlyArray<T> |
Returns T[][]
Example
arr.cartesian(["S", "M"], ["red", "blue"]);
// [["S", "red"], ["S", "blue"], ["M", "red"], ["M", "blue"]]nc.arr.times()
times<T>(n: number, value?: T | ((index: number) => T) | undefined): T[]An array of n items, from a value or a function of the index.
Parameters
| Name | Type |
|---|---|
n | number |
valueoptional | T | ((index: number) => T) |
Returns T[]
Example
arr.times(3, "x"); // ["x", "x", "x"]
arr.times(3, (i) => i * 2); // [0, 2, 4]nc.arr.sumBy()
sumBy<T>(array: readonly T[], iteratee?: Iteratee<T>): numberAdds up a number from each item. Non-numbers count as 0.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
iterateeoptional | Iteratee<T> |
Returns number
Example
arr.sumBy(cart, "price");
arr.sumBy(cart, (item) => item.price * item.quantity);nc.arr.averageBy()
averageBy<T>(array: readonly T[], iteratee?: Iteratee<T>): numberThe average of a number from each item, or 0 for an empty array.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
iterateeoptional | Iteratee<T> |
Returns number
Example
arr.averageBy(reviews, "rating"); // 4.3nc.arr.maxBy()
maxBy<T>(array: readonly T[], iteratee?: Iteratee<T>): T | undefinedThe item with the highest value.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
iterateeoptional | Iteratee<T> |
Returns T | undefined
Example
arr.maxBy(players, "score");nc.arr.minBy()
minBy<T>(array: readonly T[], iteratee?: Iteratee<T>): T | undefinedThe item with the lowest value.
Parameters
| Name | Type |
|---|---|
array | readonly T[] |
iterateeoptional | Iteratee<T> |
Returns T | undefined
Example
arr.minBy(products, "price");Types
Import any of them in TypeScript with import type { Iteratee } from "@ix-xs/node-comfort", or in JavaScript with import("@ix-xs/node-comfort").Iteratee.
Iteratee
How to get a value out of an item: a property name, or a function (item, index) => value.
type Iteratee = (T extends object ? keyof T & (string | number) : never) | ((item: T, index: number) => unknown)Page
A page returned by paginate().
| Property | Type | Description |
|---|---|---|
items | T[] | The items on this page. |
page | number | The page number, starting at 1. |
perPage | number | |
total | number | Total number of items. |
pages | number | Number of pages, at least 1. |
hasPrev | boolean | |
hasNext | boolean |
SortByOptions
Options for sortBy().
| Property | Type | Description |
|---|---|---|
orderoptional | "asc" | "desc" | Direction for keys that don't set their own. Defaults to "asc". |
naturaloptional | boolean | Sort text like a person would: ignore case, respect accents, "item2" before "item10". |
localeoptional | string | Language used by natural. Defaults to the system's. |
SortKey
A sort key for sortBy(): an iteratee, or [iteratee, "asc" | "desc"] to give that key its own direction.