npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

list-fns

v1.4.0

Published

A collection of utility functions to be used with .map, .filter, .sort and .reduce

Downloads

484

Readme

list-fns

npm version

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 }]
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]]
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]
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
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]
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 }]
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 }]
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]
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 }]
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 }]
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]
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 }]}
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"] }] }
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" }] }
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" }]
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]
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 }]
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 }]
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]
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 }]
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]
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]
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 }]
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]
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 }]
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]
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 }]
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
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 }
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 }
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
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 }
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 }
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]
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 }]]
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 }]
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 }]
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 }]
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 }]
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
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
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
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]
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 }]
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 }]
const uniqueByProperty = <
  TObject extends object,
  TKey extends keyof TObject
>(
  key: TKey
) => uniqueBy<TObject>(get(key))