1.4.0 • Published 3 years ago

list-fns v1.4.0

Weekly downloads
161
License
MIT
Repository
github
Last release
3 years ago

list-fns

npm version npm.io npm.io

This library contains higher order functions that simplify common list operations, similar to what you'd find in lodash or ramda. Unlike these libraries, list-fns is designed specifically to be used with the native array methods.

These functions have not been rigorously tested for performance so they are currently not recommended for use with large datasets.

Example

import { byProperty, get, uniqueByProperty } from "list-fns";

const people = [
  { name: "Jack", age: 44 },
  { name: "Jack", age: 60 },
  { name: "Jane", age: 20 },
];

// Inline implementation:
people
  .filter(
    (person, index) => index === people.findIndex(p => p.name === person.name)
  )
  .sort((a, b) => (a.age < b.age ? -1 : a.age > b.age ? 1 : 0))
  .map(person => person.name); // ["Jane", "Jack"]

// With list functions:
people
  .filter(uniqueByProperty("name"))
  .sort(byProperty("age"))
  .map(get("name")); // ["Jane", "Jack"]

Install

npm install list-fns

A note about sorting

This library contains functions to be used with [].sort(). Always be mindful of the fact that .sort() and .reverse() will mutate the original list. If .sort() is the first method you're calling on a list you should probably clone it first in order to avoid unexpected behavior:

[...list].sort();
list.slice().sort();
[].concat(list).sort();

Functions

by

by: <T>(func: (el: T) => any) => (a: T, b: T) => 0 | 1 | -1

Use with: sort

Sort the elements by func(element) . Supports sorting by boolean values (elements that are true first).

[{ a: 2 }, { a: 1 }].sort(by(el => el.a)); // Returns [{ a: 1 }, { a: 2 }]

byProperty

byProperty: <TObject extends object, TKey extends keyof TObject>(key: TKey) => (a: TObject, b: TObject) => 0 | 1 | -1

Use with: sort

Sort the elements by element[key] (can also be an array index). Supports sorting by boolean values (elements that are true first).

[{ a: 2 }, { a: 1 }].sort(byProperty('a')); // Returns [{ a: 1 }, { a: 2 }]
[["a", 2], ["a", 1]].sort(byProperty(1)); // Returns [["a", 1], ["a", 2]]

byValue

byValue: (a: number, b: number) => 0 | 1 | -1

Use with: sort

Sort a list of numbers. This is useful because javascript sorts numbers as string, meaning that 25, 100 results in 100, 25 since "2" is greater than "1"

[100, 25].sort(); // Returns [100, 25]
[100, 25].sort(byValue); // Returns [25, 100]

countBy

countBy: <T>(func: (el: T) => boolean) => (acc: number, el: T) => number

Use with: reduce

Returns the number of times func returned true for the list elements. A number must be passed to the second argument of reduce . Can be combined with boolean-returning functions like is , isnt , propertyIs or propertyIsOneOf .

["a", "a", "b"].reduce(countBy(el => el === "a"), 0); // Returns 2
["a", "a", "b"].reduce(countBy(is("a")), 0); // Returns 2

duplicates

duplicates: (el: unknown, _: number, list: unknown[]) => boolean

Use with: filter

Returns duplicates

[1, 1, 1, 2].filter(duplicates); // Returns [1, 1, 1]

duplicatesBy

duplicatesBy: <T>(func: (el: T) => unknown) => (el: T, _: number, list: T[]) => boolean

Use with: filter

Returns all duplicates compared by func(element)

[{ a: 1 }, { a : 1 }, { a: 2 }].filter(duplicatesBy(el => el.a)); // Returns [{ a: 1 }, { a: 1 }]

duplicatesByProperty

duplicatesByProperty: <TObject extends object, TKey extends keyof TObject>(key: TKey) => (el: TObject, _: number, list: TObject[]) => boolean

Use with: filter

Returns duplicates compared by element[key]

[{ a: 1 }, { a: 1 }].filter(duplicatesByProperty('a')); // Return [{ a: 1 }, { a: 1 }]

exclude

exclude: <T>(list: T[]) => (el: T) => boolean

Use with: filter

Removes the provided elements from the list

[1, 2, 3, 4].filter(exclude([1, 2])); // Returns [3, 4]

excludeBy

excludeBy: <T>(func: (el: T) => unknown, list: T[]) => (el: T) => boolean

Use with: filter

Removes the provided elements from the list compared by running func on elements in both lists

[{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }]
  .filter(excludeBy(el => el.a, [{ a: 1 }, { a: 2 }]));
  // Returns [{ a: 3 }, { a: 4 }]

excludeByProperty

excludeByProperty: <TObject extends object, TKey extends keyof TObject>(key: TKey, list: TObject[]) => (el: TObject) => boolean

Use with: filter

Removes the provided elements from the list compared at key

[{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }]
  .filter(excludeByProperty('a', [{ a: 1 }, { a: 2 }]));
  // Returns [{ a: 3 }, { a: 4 }]

get

get: {
  <TObject extends object, TKey1 extends keyof TObject, TKey2 extends keyof TObject[TKey1], TKey3 extends keyof TObject[TKey1][TKey2]>(key1: TKey1, key2: TKey2, key3: TKey3): (obj: TObject) => TObject[TKey1][TKey2][TKey3];
  <TObject extends object, TKey1 extends keyof TObject, TKey2 extends keyof TObject[TKey1]>(key1...;
}

Use with map or filter

Returns element[key] (can also be an array index). Supports up to three keys of depth.

[{ a: 1 }, { a: 2 }].map(get('a')); // Returns [1, 2]
[["a", 1], ["a", 2]].map(get(1)); // Returns [1, 2]
[{ a: { b: { c: 1 } } }].map(get('a', 'b', 'c')); // Returns [1]

groupBy

groupBy: <K extends string, V>(func: (el: V) => K | undefined) => (acc: Record<K, V[]>, el: V) => Record<K, V[]>

Use with: reduce

Given a key-returning function, returns the elements grouped in an object according to the returned keys. A second argument must be passed to reduce . For javascript an empty object is enough. For typescript an object with properties or a type cast may be required.

[{ age: 10 }, { age: 80 }].reduce(
  groupBy(el => (el.age > 30 ? "old" : "young")),
  { old: [], young: [] }
); // Returns { old: [{ age: 80 }], young: [{ age: 10 }]}

groupByMany

groupByMany: <K extends string, V>(func: (el: V) => K[] | undefined) => (acc: Record<K, V[]>, el: V) => Record<K, V[]>

Use with: reduce

Given a function func that returns a list of keys, returns an object containing the elements grouped by the returned keys. Unlike the groupBy function, elements can appear several times in this object. Good for grouping objects by properties that are arrays. An empty object must be passed as the second argument to reduce

const b1: B = { items: ["a", "b"] };
const b2: B = { items: ["a"] };

[b1, b2].reduce(groupByMany(b => b.items), {});
// Returns { a: [{ items: ["a", "b"] }, { items: ["a"] }], b: [{ items: ["b"] }] }

groupByProperty

groupByProperty: <K extends keyof V, V extends { [key: string]: any; }>(key: K) => (acc: Record<V[K], V[]>, el: V) => Record<V[K], V[]>

Use with: reduce

Given a property name, returns an object containing the elements grouped by the values for that property. A second argument must be passed to reduce . For javascript an empty object is enough. For typescript an object with properties or a type cast may be required.

[{ name: "Jane" }, { name: "John" }].reduce(
  groupByProperty("name"),
  {}
); // Returns { Jane: [{ name: "Jane" }], John: [{ name: "John" }] }

has

has: <TObject extends object, TKey extends keyof TObject>(...keys: TKey[]) => (object: TObject) => object is TObject & HasProperties<TObject, TKey>

Use with: find , filter

Returns true for elements where element[key] for all provided keys is defined. This is useful when properties are needed but optional in the element type.

Known limitations: Type inference doesn't always work when list elements have an inferred type.

type Person = { name?: string };
const people: Person[] = [{ name: "John" }, {}];
people.filter(has("name")); // Returns [{ name: "a" }]

intersection

intersection: <T>(list: T[]) => (el: T) => boolean

Use with: filter

Returns a list of elements that are present in both lists

[1, 2, 3].filter(intersection([2, 3, 4])); // Returns [2, 3]

intersectionBy

intersectionBy: <T>(func: (el: T) => unknown, list: T[]) => (el: T) => boolean

Use with: filter

Returns a list of elements that are present in both lists compared by running func on elements in both lists

[{ a: 1 }, { a: 2 }, { a: 3 }]
  .filter(intersectionBy(el => el.a, [{ a: 2 }, { a: 3 }, { a: 4 }]));
  // Returns [{ a: 2 }, { a: 3 }]

intersectionByProperty

intersectionByProperty: <TObject extends object, TKey extends keyof TObject>(key: TKey, list: TObject[]) => (el: TObject) => boolean

Use with: filter

Returns a list of elements that are present in both lists compared at key

[{ a: 1 }, { a: 2 }, { a: 3 }]
  .filter(intersectionByProperty("a", [{ a: 2 }, { a: 3 }, { a: 4 }]));
  // Returns [{ a: 2 }, { a: 3 }]

is

is: <T>(value: T) => (el: T) => boolean

Use with: find , filter

Returns true for elements that are equal to value

[1,2,3].find(is(1)); // Returns 1
[1,1,2].filter(is(1)); // Returns [1, 1]

isBy

isBy: <T, U>(func: (el: T) => U, value: U) => (el: T) => boolean

Use with: find , filter

Returns true for elements where func(element) equals value

[{ a: 1 }, { a: 2 }].find(isBy(el => el.a, 2)); // Returns { a: 2 }
[{ a: 1 }, { a: 2 }].filter(isBy(el => el.a, 2)); // Returns [{ a: 2 }]

isDefined

isDefined: <T>(x: T) => x is NonNullable<T>

Use with: filter

Remove elements that are undefined or null

[1, null, undefined, 2].filter(isDefined); // Returns [1, 2]

isOneOf

isOneOf: <T>(list: T[]) => (el: T) => boolean

Use with: find , filter

Alias for intersection . Returns true for elements that exist in the provided list

[1,1,2,2,3].filter(isOneOf([2,3])); // Returns [2, 2, 3]

isOneOfBy

isOneOfBy: <T, U>(func: (el: T) => U, list: U[]) => (el: T) => boolean

Use with: find , filter

Returns true for elements where func(element) exists in list

[{ a: 1 }, { a: 2 }, { a: 3 }].find(isOneOfBy(el => el.a, [2, 3]));
// ^ Returns { a: 2 }
[{ a: 1 }, { a: 2 }, { a: 3 }].filter(isOneOfBy(el => el.a, [2, 3]));
// ^ Returns [{ a: 2 }, { a: 3 }]

isnt

isnt: <T>(value: T) => (el: T) => boolean

Use with: find , filter

Returns true for elements that are not equal to value

[1,2,3].find(isnt(1)); // Returns 2
[1,2,2].filter(isnt(1)); // Returns [2,2]

isntBy

isntBy: <T, U>(func: (el: T) => U, value: U) => (el: T) => boolean

Use with: find , filter

Returns true for elements where func(element) does not equal value

[{ a: 1 }, { a: 2 }].find(isntBy(el => el.a, 2)); // Returns { a: 1 }
[{ a: 1 }, { a: 2 }].filter(isntBy(el => el.a, 2)); // Returns [{ a: 1 }]

isntOneOf

isntOneOf: <T>(list: T[]) => (el: T) => boolean

Use with: find , filter

Alias for exclude . Returns true for elements that do not exist in the provided list

[1,1,2,2,3].filter(isntOneOf([2,3])); // Returns [1, 1]

isntOneOfBy

isntOneOfBy: <T, U>(func: (el: T) => U, list: U[]) => (el: T) => boolean

Use with: find , filter

Returns true for elements where func(element) exists in list

[{ a: 1 }, { a: 2 }, { a: 3 }].find(isntOneOfBy(el => el.a, [2, 3]));
// ^ Returns { a: 1 }
[{ a: 1 }, { a: 2 }, { a: 3 }].filter(isntOneOfBy(el => el.a, [2, 3]));
// ^ Returns [{ a: 1 }]

max

max: (acc: number, el: number) => number

Use with: reduce

Returns the largest value in the list

[1,2,3,4].reduce(max); // Returns 4

maxBy

maxBy: <T>(func: (el: T) => number) => (acc: T, el: T) => T

Use with: reduce

Returns the largest element by comparing func(element)

[{ a: 1 }, { a: 2 }, { a: 3 }].reduce(maxBy(el => el.a)); // Returns { a: 3 }

maxByProperty

maxByProperty: <TObject extends object, TKey extends keyof TObject>(key: TKey) => (acc: TObject, el: TObject) => TObject

Use with: reduce

Returns the largest element by comparing element[key]

[{ a: 1 }, { a: 2 }, { a: 3 }].reduce(maxByProperty("a")); // Returns { a: 3 }

min

min: (acc: number, el: number) => number

Use with: reduce

Returns the smallest value in the list

[1,2,3,4].reduce(min); // Returns 1

minBy

minBy: <T>(func: (el: T) => number) => (acc: T, el: T) => T

Use with: reduce

Returns the smallest element by comparing func(element)

[{ a: 1 }, { a: 2 }, { a: 3 }].reduce(minBy(el => el.a)); // Returns { a: 1 }

minByProperty

minByProperty: <TObject extends object, TKey extends keyof TObject>(key: TKey) => (acc: TObject, el: TObject) => TObject

Use with: reduce

Returns the smallest element by comparing element[key]

[{ a: 1 }, { a: 2 }, { a: 3 }].reduce(minByProperty("a")); // Returns { a: 1 }

or

or: <T>(fallback: NonNullable<T>) => (x: T) => NonNullable<T>

Use with: map

Replaces list elements that are undefined or null with fallback

[1, null, undefined, 2].map(or(0)); // Returns [1, 0, 0, 2]

partition

partition: <T>(func: (el: T) => boolean) => (acc: T[][], el: T) => T[][]

Use with: reduce

Splits the input list into two lists. The first list contains elements for which the given function returned true , the second contains elements for which the function returned false .

[{ age: 10 }, { age: 80 }].reduce(partition(el => el.age > 30), []);
// Returns [[{ age: 80 }], [{ age: 10 }]]

propertyIs

propertyIs: <TObject extends object, TKey extends keyof TObject>(key: TKey, value: TObject[TKey]) => (el: TObject) => boolean

Use with: find , filter

Returns true for elements where element[key] equals value

[{ a: 1 }, { a: 2 }].find(propertyIs("a", 2)); // Returns { a: 2 }
[{ a: 1 }, { a: 2 }].filter(propertyIs("a", 2)) // Returns [{ a: 2 }]

propertyIsOneOf

propertyIsOneOf: <TObject extends object, TKey extends keyof TObject>(key: TKey, list: TObject[TKey][]) => (el: TObject) => boolean

Use with: find , filter

Returns true for elements where element[key] exists in list

[{ a: 1 }, { a: 2 }, { a: 3 }].find(propertyIsOneOf("a", [2, 3]));
// ^ Returns { a: 2 }
[{ a: 1 }, { a: 2 }, { a: 3 }].filter(propertyIsOneOf("a", [2, 3]));
// ^ Returns [{ a: 2 }, { a: 3 }]

propertyIsnt

propertyIsnt: <TObject extends object, TKey extends keyof TObject>(key: TKey, value: TObject[TKey]) => (el: TObject) => boolean

Use with: find , filter

Returns true for elements where element[key] does not equal value

[{ a: 1 }, { a: 2 }].find(propertyIsnt("a", 2)); // Returns { a: 1 }
[{ a: 1 }, { a: 2 }].filter(propertyIsnt("a", 2)); // Returns [{ a: 1 }]

propertyIsntOneOf

propertyIsntOneOf: <TObject extends object, TKey extends keyof TObject>(key: TKey, list: TObject[TKey][]) => (el: TObject) => boolean

Use with: find , filter

Returns true for elements where element[key] exists in list

[{ a: 1 }, { a: 2 }, { a: 3 }].find(propertyIsntOneOf("a", [2, 3]));
// ^ Returns { a: 1 }
[{ a: 1 }, { a: 2 }, { a: 3 }].filter(propertyIsntOneOf("a", [2, 3]));
// ^ Returns [{ a: 1 }]

sum

sum: (acc: number, element: number) => number

Use with: reduce

Sum a list of numbers

[1, 2, 3].reduce(sum); // Returns 6

sumBy

sumBy: {
  <T>(func: (el: T) => number): (acc: number, el: T) => number;
  <T>(func: (el: number) => number): (acc: number, el: number) => number;
}

Use with: reduce

Sums the values by applying func to elements. If the list elements aren't numbers, a number must be passed as the second argument to reduce .

[{ a: 1 }, { a: 2 }].reduce(sumBy(el => el.a), 0); // Returns 3
[1.5, 2.5].reduce(sumBy(Math.floor)); // Returns 3

sumByProperty

sumByProperty: <TObject extends { [key: string]: number; }, TKey extends keyof TObject>(key: TKey) => (acc: number, el: TObject) => number

Use with: reduce

Sums the values of element[key] for all elements. A number must be passed to the second argument of reduce .

[{ a: 1 }, { a: 2 }].reduce(sumByProperty('a'), 0); // Returns 3

unique

unique: (el: unknown, index: number, list: unknown[]) => boolean

Use with: filter

Removes duplicates from list

[1,1,1,2].filter(unique); // Returns [1, 2]

uniqueBy

uniqueBy: <T>(func: (el: T) => unknown) => (el: T, index: number, list: T[]) => boolean

Use with: filter

Removes duplicates compared by func(element)

[{ a: 1 }, { a : 1 }].filter(uniqueBy(el => el.a)); // Returns [{ a: 1 }]

uniqueByProperty

uniqueByProperty: <TObject extends object, TKey extends keyof TObject>(key: TKey) => (el: TObject, index: number, list: TObject[]) => boolean

Use with: filter

Removes duplicates compared by element[key]

[{ a: 1 }, { a: 1 }].filter(uniqueByProperty('a')); // Return [{ a: 1 }]
1.4.0

3 years ago

1.3.1

3 years ago

1.3.0

3 years ago

1.2.0

3 years ago

1.1.0

3 years ago

1.0.0

4 years ago

1.0.0-beta.1

4 years ago

0.3.0

4 years ago

0.2.0

4 years ago

0.1.0

4 years ago