![Introducing Enhanced Alert Actions and Triage Functionality](https://cdn.sanity.io/images/cgdhsj6q/production/fe71306d515f85de6139b46745ea7180362324f0-2530x946.png?w=800&fit=max&auto=format)
Product
Introducing Enhanced Alert Actions and Triage Functionality
Socket now supports four distinct alert actions instead of the previous two, and alert triaging allows users to override the actions taken for all individual alerts.
list-fns
Advanced tools
Changelog
1.2.0
has
Readme
This library contains higher order functions for doing common list operations to enable a more declarative style when working with lists. File size is prioritized over performance (several of the functions are O(n^2)); this is not recommended for use with very large datasets.
Example
import { byProperty, get, uniqueByProperty } from "list-fns";
const people = [
{ name: "Jack", age: 44 },
{ name: "Jack", age: 60 },
{ name: "Jane", age: 20 },
];
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"]
people
.filter(uniqueByProperty("name"))
.sort(byProperty("age"))
.map(get("name")); // ["Jane", "Jack"]
npm install list-fns
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();
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: <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: (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: <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: (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: <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[]
) => numberOfOccurencesBy(list, el, func) > 1
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: <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: <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: <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: {
<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: <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 an object of lists of elements. 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 is 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;
const group: V[] = acc[groupName] || [];
return Object.assign({}, acc, { [groupName]: group.concat(el) });
}
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 of lists of 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 is 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;
const group: V[] = acc[groupName] || [];
return Object.assign({}, acc, { [groupName]: group.concat(el) });
}
has: <TObject extends object, TKey extends keyof TObject>(...keys: TKey[]) => (object: TObject) => object is TObject & Required<Pick<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 & Required<Pick<TObject, TKey>> =>
keys.every(key => isDefined(object[key]))
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: <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: <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: <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: <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: <T>(x: T | undefined) => x is T
Use with: filter
Removes elements that are undefined
[1, undefined, 2].filter(isDefined); // Returns [1, 2]
const isDefined = <T>(x: T | undefined): x is T =>
typeof x !== "undefined"
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: <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: <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: <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: <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: <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: (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: <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: <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: (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: <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: <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: <T>(fallback: T) => (x: T | undefined) => T
Use with: map
Replaces list elements that are undefined
with fallback
[1, undefined, 2].map(or(0)); // Returns [1, 0, 2]
const or = <T>(fallback: T) => (x: T | undefined): T =>
isDefined(x) ? x : fallback
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] || [];
return func(el) ? [a0.concat(el), a1] : [a0, a1.concat(el)];
}
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: <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: <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: <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: (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: {
<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: <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: (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: <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: <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))
FAQs
A collection of utility functions to be used with .map, .filter, .sort and .reduce
The npm package list-fns receives a total of 38 weekly downloads. As such, list-fns popularity was classified as not popular.
We found that list-fns demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket now supports four distinct alert actions instead of the previous two, and alert triaging allows users to override the actions taken for all individual alerts.
Security News
Polyfill.io has been serving malware for months via its CDN, after the project's open source maintainer sold the service to a company based in China.
Security News
OpenSSF is warning open source maintainers to stay vigilant against reputation farming on GitHub, where users artificially inflate their status by manipulating interactions on closed issues and PRs.