list-fns
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
Table of contents
- by
- byProperty
- byValue
- countBy
- duplicates
- duplicatesBy
- duplicatesByProperty
- exclude
- excludeBy
- excludeByProperty
- get
- groupBy
- groupByMany
- groupByProperty
- has
- intersection
- intersectionBy
- intersectionByProperty
- is
- isBy
- isDefined
- isOneOf
- isOneOfBy
- isnt
- isntBy
- isntOneOf
- isntOneOfBy
- max
- maxBy
- maxByProperty
- min
- minBy
- minByProperty
- or
- partition
- propertyIs
- propertyIsOneOf
- propertyIsnt
- propertyIsntOneOf
- sum
- sumBy
- sumByProperty
- unique
- uniqueBy
- uniqueByProperty
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 }]
Implementation
const by = <T>(func: (el: T) => any) => (a: T, b: T) => {
const A = func(a),
B = func(b);
if (typeof A === "boolean") return A && !B ? -1 : !A && B ? 1 : 0;
return A < B ? -1 : A > B ? 1 : 0;
}
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]]
Implementation
const byProperty = <TObject extends object, TKey extends keyof TObject>(
key: TKey
) => by<TObject>(get(key))
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]
Implementation
const byValue = (a: number, b: number) => (a < b ? -1 : a > b ? 1 : 0)
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
Implementation
const countBy = <T>(func: (el: T) => boolean) => (acc: number, el: T) =>
acc + (func(el) ? 1 : 0)
duplicates
duplicates: (el: unknown, _: number, list: unknown[]) => boolean
Use with: filter
Returns duplicates
[1, 1, 1, 2].filter(duplicates); // Returns [1, 1, 1]
Implementation
const duplicates = duplicatesBy(el => el)
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 }]
Implementation
const duplicatesBy = <T>(func: (el: T) => unknown) => (
el: T,
_: number,
list: T[]
) => {
let n = 0;
for (let i = 0; i < list.length; i++) {
if (n >= 2) return true;
if (func(list[i]) === func(el)) n++;
}
return false;
}
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 }]
Implementation
const duplicatesByProperty = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey
) => duplicatesBy<TObject>(get(key))
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]
Implementation
const exclude = <T>(list: T[]) => (el: T) =>
findIndex(list, a => a === el) === -1
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 }]
Implementation
const excludeBy = <T>(func: (el: T) => unknown, list: T[]) => (el: T) =>
findIndex(list, a => func(a) === func(el)) === -1
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 }]
Implementation
const excludeByProperty = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey,
list: TObject[]
) => excludeBy(get(key), list)
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]
Implementation
export function 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) {
return (obj: TObject) => {
if (key3 && key2)
return obj && obj[key1] && obj[key1][key2] && obj[key1][key2][key3];
if (key2) return obj && obj[key1] && obj[key1][key2];
return obj && obj[key1];
};
}
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 }]}
Implementation
const groupBy = <K extends string, V>(
func: (el: V) => K | undefined
) => (acc: Record<K, V[]>, el: V): Record<K, V[]> => {
const groupName = func(el);
if (!groupName) return acc;
if (!acc[groupName]) acc[groupName] = [];
acc[groupName].push(el);
return acc;
}
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"] }] }
Implementation
const groupByMany = <K extends string, V>(
func: (el: V) => K[] | undefined
) => (acc: Record<K, V[]>, el: V): Record<K, V[]> => {
const groupNames = func(el) || [];
groupNames.forEach(key => {
if (!acc[key]) acc[key] = [];
acc[key].push(el);
});
return acc;
}
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" }] }
Implementation
const groupByProperty = <
K extends keyof V,
V extends { [key: string]: any }
>(
key: K
) => (acc: Record<V[K], V[]>, el: V): Record<V[K], V[]> => {
const groupName = el[key];
if (!groupName) return acc;
if (!acc[groupName]) acc[groupName] = [];
acc[groupName].push(el);
return acc;
}
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" }]
Implementation
const has = <TObject extends object, TKey extends keyof TObject>(
...keys: TKey[]
) => (object: TObject): object is TObject & HasProperties<TObject, TKey> =>
keys.every(key => isDefined(object[key]))
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]
Implementation
const intersection = <T>(list: T[]) => (el: T) =>
findIndex(list, a => a === el) !== -1
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 }]
Implementation
const intersectionBy = <T>(func: (el: T) => unknown, list: T[]) => (
el: T
) => findIndex(list, a => func(a) === func(el)) !== -1
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 }]
Implementation
const intersectionByProperty = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey,
list: TObject[]
) => intersectionBy(get(key), list)
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]
Implementation
const is = <T>(value: T) => (el: T) => el === value
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 }]
Implementation
const isBy = <T, U>(func: (el: T) => U, value: U) => (el: T) =>
func(el) === value
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]
Implementation
const isDefined = <T>(x: T): x is NonNullable<T> =>
x !== undefined && x !== null
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]
Implementation
const isOneOf = intersection
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 }]
Implementation
const isOneOfBy = <T, U>(func: (el: T) => U, list: U[]) => (el: T) =>
findIndex(list, a => a === func(el)) !== -1
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]
Implementation
const isnt = <T>(value: T) => (el: T) => el !== value
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 }]
Implementation
const isntBy = <T, U>(func: (el: T) => U, value: U) => (el: T) =>
func(el) !== value
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]
Implementation
const isntOneOf = exclude
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 }]
Implementation
const isntOneOfBy = <T, U>(func: (el: T) => U, list: U[]) => (el: T) =>
findIndex(list, a => a === func(el)) === -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
Implementation
const max = (acc: number, el: number) => Math.max(acc, el)
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 }
Implementation
const maxBy = <T>(func: (el: T) => number) => (acc: T, el: T) =>
func(el) > func(acc) ? el : acc
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 }
Implementation
const maxByProperty = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey
) => (acc: TObject, el: TObject) => (el[key] > acc[key] ? el : acc)
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
Implementation
const min = (acc: number, el: number) => Math.min(acc, el)
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 }
Implementation
const minBy = <T>(func: (el: T) => number) => (acc: T, el: T) =>
func(el) < func(acc) ? el : acc
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 }
Implementation
const minByProperty = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey
) => (acc: TObject, el: TObject) => (el[key] < acc[key] ? el : acc)
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]
Implementation
const or = <T>(fallback: NonNullable<T>) => (x: T): NonNullable<T> =>
isDefined(x) ? x : fallback
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 }]]
Implementation
const partition = <T>(func: (el: T) => boolean) => (
acc: T[][],
el: T
) => {
const a0 = acc[0] || [],
a1 = acc[1] || [];
if (func(el)) a0.push(el);
else a1.push(el);
return [a0, a1];
}
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 }]
Implementation
const propertyIs = <TObject extends object, TKey extends keyof TObject>(
key: TKey,
value: TObject[TKey]
) => isBy(get(key), value)
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 }]
Implementation
const propertyIsOneOf = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey,
list: TObject[TKey][]
) => isOneOfBy(get(key), list)
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 }]
Implementation
const propertyIsnt = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey,
value: TObject[TKey]
) => isntBy(get(key), value)
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 }]
Implementation
const propertyIsntOneOf = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey,
list: TObject[TKey][]
) => isntOneOfBy(get(key), list)
sum
sum: (acc: number, element: number) => number
Use with: reduce
Sum a list of numbers
[1, 2, 3].reduce(sum); // Returns 6
Implementation
const sum = (acc: number, element: number) => acc + element
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
Implementation
export function sumBy<T>(func: (el: T | number) => number) {
return (acc: number, el: T | number) =>
typeof el === "number" ? func(acc) + func(el) : acc + func(el);
}
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
Implementation
const sumByProperty = <
TObject extends { [key: string]: number },
TKey extends keyof TObject
>(
key: TKey
) => (acc: number, el: TObject) => acc + el[key]
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]
Implementation
const unique = uniqueBy(el => el)
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 }]
Implementation
const uniqueBy = <T>(func: (el: T) => unknown) => (
el: T,
index: number,
list: T[]
) => index === findIndex(list, t => func(t) === func(el))
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 }]
Implementation
const uniqueByProperty = <
TObject extends object,
TKey extends keyof TObject
>(
key: TKey
) => uniqueBy<TObject>(get(key))