pointless-js
Version:
Functional point-free utilities for js
326 lines (292 loc) • 11.3 kB
text/typescript
import { add } from './number';
import { safeObj } from './object';
import { compose } from './compose';
import { id, createSafeOperation, ifNull, Comparator } from './generic';
import { Nullable } from './type';
export const safeArr = createSafeOperation<Array<any>>([]);
/**
* Lazy version of Array.prototype.map()
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the callback to an empty array.
*
* @param f - A callback for the map function.
* @returns A function that maps an array into a different array using the remembered callback.
*/
export function map<T, R>(f: (x: T) => R) {
return safeArr<T[], R[]>(_ => _.map<R>(f));
}
/**
* Lazy version of Array.prototype.reduce()
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the callback to an empty array.
*
* @param f - A callback for the reduce function.
* @returns A function that reduces an array using the remembered callback.
*/
export function reduce<T>(f: (p: T, c: T, i: number, arr: Readonly<T[]>) => T): (arr: Readonly<Nullable<T[]>>) => T;
/**
* Lazy version of Array.prototype.reduce()
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the callback to an empty array.
*
* @param f - A callback for the reduce function.
* @param init - A callback for the reduce function.
* @returns A function that reduces an array using the remembered callback.
*/
export function reduce<U, T>(f: (p: U, c: T, i: number, arr: Readonly<T[]>) => U, init?: U | undefined): (arr: Readonly<Nullable<T[]>>) => U;
/**
* Lazy version of Array.prototype.reduce()
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the callback to an empty array.
*
* @param f - A callback for the reduce function.
* @param [init] - [optional] A callback for the reduce function.
* @returns A function that reduces an array using the remembered callback.
*/
export function reduce<U, T>(f: (p: U, c: T, i: number, arr: Readonly<T[]>) => U, init: U): (arr: Readonly<Nullable<T[]>>) => U;
export function reduce<U, T = U>(f: (p: U, c: T, i: number, arr: Readonly<T[]>) => U, init?: U): (arr: Readonly<Nullable<T[]>>) => U | T {
return typeof init !== 'undefined'
? safeArr<T[], U>(_ => _.reduce(f, init))
: safeArr<T[], T>(_ => _.reduce(f as any));
}
/**
* Lazy version of Array.prototype.reduceRight()
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the callback to an empty array.
*
* @param f - A callback for the reduce function.
* @param [init] - [optional] A callback for the reduce function.
* @returns A function that reduces an array backwards using the remembered callback.
*/
export function reduceRight<U, T>(f: (p: U, c: T, i: number, arr: Readonly<T[]>) => U, init: U) {
return safeArr<T[], U>(_ => _.reduceRight(f, init));
}
/**
* Lazy version of Array.prototype.filter()
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the callback to an empty array.
*
* @param f - A callback for the filter function, should return a boolean.
* @returns A function that filters an array using the remembered callback.
*/
export function filter<T>(f: (x: T, i: number, arr: readonly T[]) => boolean): (arr: Nullable<Readonly<T[]>>) => T[];
/**
* Lazy version of Array.prototype.filter()
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the callback to an empty array.
*
* @param f - A callback for the filter function, should return a boolean.
* @returns A function that filters an array using the remembered callback.
*/
export function filter<T, R extends T>(f: (x: T, i: number, arr: readonly T[]) => x is R): (arr: Nullable<Readonly<T[]>>) => R[];
export function filter<T>(f: (x: T, i: number, arr: readonly T[]) => boolean): (arr: Nullable<Readonly<T[]>>) => T[] {
return safeArr<T[], T[]>(_ => _.filter(f));
}
export function isOneOf<T>(...many: T[]) {
return (x: T) => many.includes(x);
}
/**
* Lazy version of Array.prototype.join()
*
* Remembers the separator to join the array by it later on.
*
* In case of array being `null` or `undefined` just applies the callback to an empty array.
*
* @param separator - A string to join by.
* @returns A function that joins an array using the remembered separator.
*/
export function join(separator: string = '') {
return safeArr(_ => _.join(separator));
}
/**
* Pure version of Array.prototype.reverse()
*
* Doesn't modify the original array, returns a fresh and reversed one.
*
* In case of original array being `null` or `undefined` just returns an empty array.
*
* @param arr - Array to reverse.
* @returns A new array with reverse order of elements.
*/
export function reverse<T>(arr?: T[] | null) {
return reduceRight<T[], T>((arr, el) => (arr.push(el), arr), [])(arr);
}
/**
* Pure lazy version of Array.prototype.sort():
* - if a callback returns a negative number - make `a` lower than `b`
* - if a callback returns a 0 - leave order unchanged
* - if a callback returns a positive number - make `b` lower than `a`
*
* Doesn't modify the original array, returns a fresh and sorted one.
*
* In case of original array being `null` or `undefined` just returns an empty array.
*
* @param f - a compare function.
* @returns A function that accepts an array to sort.
*/
export function sort<T>(f?: (a: T, b: T) => number) {
return safeArr<T[], T[]>(_ => _.slice().sort(f));
}
/**
* Lazy version of Array.prototype.slice(), which returns a section of an array.
*
* Remembers the arguments to apply them to the array.slice() later on.
*
* In case of array being `null` or `undefined` just applies the `slice()` to an empty array.
*
* @param [start] - The beginning of the specified portion of the array.
* @param [end] - The end of the specified portion of the array.
* @returns A function that slices an array using the remembered arguments.
*/
export function slice<T>(start?: number, end?: number) {
return safeArr<T[], T[]>(_ => _.slice(start, end));
}
/**
* Lazy version of Array.prototype.some(),
* which returns true if the callback yields `true` for at least one element of the array.
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the `some()` to an empty array.
*
* @param f - A callback for the `some()` function, should return a boolean.
* @returns A function that checks the array using the remembered callback.
*/
export function some<T>(f: (x: T) => boolean) {
return safeArr<T[], boolean>(_ => _.some(f));
}
/**
* Lazy version of Array.prototype.every(),
* which returns true if the callback yields `true` for every element of the array.
*
* Remembers the callback to apply it to the array later on.
*
* In case of array being `null` or `undefined` just applies the `every()` to an empty array.
*
* @param f - A callback for the `every()` function, should return a boolean.
* @returns A function that checks the array using the remembered callback.
*/
export function every<T>(f: (x: T) => boolean) {
return safeArr<T[], boolean>(_ => _.every(f));
}
export function first<T>(arr?: Nullable<T[]>): T | undefined {
return safeArr<T[], T | undefined>(_ => _[0])(arr);
}
first.or = <T>(defaultValue: T) => compose<[Nullable<T[]>], Nullable<T>, T>(
ifNull(defaultValue),
first
);
export function last<T>(arr?: Nullable<T[]>): Nullable<T> {
return safeArr<T[], T | undefined>(_ => _.length === 0 ? _[0] : _[_.length - 1])(arr);
}
last.or = <T>(defaultValue: T) => compose<[Nullable<T[]>], Nullable<T>, T>(
ifNull(defaultValue),
last
);
const isPropKey = <T = object>(v: any): v is keyof T => typeof v === 'string' || typeof v === 'number' || typeof v === 'symbol';
/**
* Provides overloads for applying some action on reducing the array.
*/
type ReduceAction<R> = {
/**
* Perform lazy action on the array
*
* @param f - A getter function to get an element suitable for action.
* @returns a function, which peforms the action on all elements in the array using the getter function `f`.
*/
<T>(f: (x: T) => R): (arr: Nullable<T[]>) => R;
/**
* Perform lazy action on the array elements' props.
*
* @param prop - A string key of the prop of suitable type in an element of the array.
* @returns a function, which peforms the action on all given props of elements in the array.
*/
<T>(prop: keyof T): (arr: Nullable<T[]>) => R;
/**
* Perform the action on the array elements.
*
* Works only on arrays of the given type.
*
* @returns a result of the action performed.
*/
<T>(arr: Nullable<T[]>): R;
};
/**
* Creates a function that performs an action on all elements of the array,
* using the `reduce()` function.
*
* @param act - A reducing action to perform on the array elements
* @param [init] - [optional] initial value for the `reduce` function
* @returns a final action function
*/
export function action<R>(act: (a: R, b: R) => R, init?: R): ReduceAction<R> {
return function <T>(
arrOrProp?: Nullable<R[]> | keyof T | ((x: T) => R)
) {
const getProp = typeof arrOrProp === 'function'
? safeObj<T, R>(arrOrProp)
: isPropKey<T>(arrOrProp)
? safeObj((_: T) => _[arrOrProp])
: id;
return reduce<R, T>((a, b) => act(a, getProp(b)), init);
} as ReduceAction<R>;
}
type SummableKeys<T> = {
[key in keyof T]: T[key] extends string | number ? key : never
}[keyof T];
/**
* Lazy summation (sum) of the array elements.
*/
export const sum: {
/**
* Lazy summation (sum) of the array elements.
*
* @param f - A getter function for a numbered element
* or a number representation of an element of the array.
* @returns a function, which sums all elements in the array using the getter function `f`.
*/
<T>(f: (x: T) => number): (arr: Nullable<T[]>) => number;
/**
* Lazy summation (sum) of the array elements' props.
*
* @param prop - A string key of the prop of type `number` in an element of the array.
* @returns a function, which sums all given props of elements in the array.
*/
<T>(prop: SummableKeys<T>): (arr: Nullable<T[]>) => number;
/**
* Summation (sum) of the array elements.
*
* Works only on arrays of numbers.
*
* @returns a sum of all numbers in the array.
*/
(arr: Nullable<number[]>): number;
} = action<number>(add, 0);
/**
* Check unique for item and return boolean
* @param equals comparing function for filter
*/
export const isElementUnique = <T>(
equals: Comparator<T> = (a, b) => a === b
) => (el: T, i: number, arr: readonly T[]) => arr.findIndex(newEl => equals(newEl, el)) === i;
/**
* Remove duplicates and return unique array
* @param equals comparing function for filter
*/
export const unique = <T>(equals: Comparator<T> = (a, b) => a === b) => filter<T>(
isElementUnique<T>(equals)
);