list-ext v1.2.0
list-ext
Extends JavaScript arrays with additional functionality.
How does it work?
list-ext attaches additional methods to the Array prototype. This makes these functions available to all arrays. list-ext itself uses Ramda under the hood for most functions e.g. [1, 2].union([2, 3])
calls R.union([1, 2], [2, 3])
.
To provided safety list-ext uses Optionals as return values where a method may not succeed e.g. [].head
will return None instead of throwing an exception.
Warning
This library extends the build-in array type. This can potentially break other libraries. So use this library with caution. If this library does not work with your project I suggest using list.
Installation
You can install 'list-ext' using NPM.
npm install --save list-ext
Example
import 'list-ext'
const delayedLength = (str: string) => new Promise<number>(resolve => {
setTimeout(() => resolve(str.length), 1000)
})
/* Plain syntax without list-ext */
const plain = Promise.all(
R.append('eeeee',
R.union(['a', 'bb', 'ccc'], ['ccc', 'dddd'])
).map(x => delayedLength(x))
).then(x => x.take(2))
/* Ramda pipe syntax */
const pipe = Promise.all(
R.pipe(
R.union(['a', 'bb', 'ccc']),
R.append('eeeee')
)(['ccc', 'dddd'])
.map(x => delayedLength(x))
).then(x => x.take(2))
/* list-ext syntax */
const listEx = ['a', 'bb', 'ccc']
.union(['ccc', 'dddd'])
.appending('eeeee')
.mapAsync(x => delayedLength(x))
.then(x => x.take(2))
Why use list-ext?
Working with lists using list-ext feels more JavaScripty compared to using ramda on itself.
Important Notice
When using NodeJS it is important to import 'list-ext' in each file you want to use it. If your using a bundler like e.g. Webpack it is enough to import it in the index file.
API Documentation
interface Array<T> {
/**
* Returns the first element. In some libraries this function is named `first`.
* @example
*
* ['fi', 'fo', 'fum'].head //=> Some 'fi'
* [].head //=> None
*/
head: Optional<T>
/**
* Returns all but the last element.
* @example
*
* [1, 2, 3].init //=> [1, 2]
* [1, 2].init //=> [1]
* [1].init //=> []
* [].init //=> []
*/
init: T[]
/**
* Returns the last element.
* @example
*
* ['fi', 'fo', 'fum'].head //=> Some 'fum'
* [].head //=> None
*/
last: Optional<T>
/**
* Returns all but the first element.
* @example
*
* [1, 2, 3].init //=> [2, 3]
* [1, 2].init //=> [2]
* [1].init //=> []
* [].init //=> []
*/
tail: T[]
/**
* Applies a function to the value at the given index, returning a new copy
* of with the element at the given index replaced with the result of the
* function application.
*
* @param index The index.
* @param fn The function to apply.
* @example
*
* ['a', 'b', 'c', 'd'].adjust(1, R.toUpper) //=> ['a', 'B', 'c', 'd']
* ['a', 'b', 'c', 'd'].adjust(-1, R.toUpper) //=> ['a', 'b', 'c', 'D']
*/
adjust(index: number, fn: (value: T) => T): T[]
/**
* Returns a new list, composed of n-tuples of consecutive elements. If `n` is
* greater than the length of the list, an empty list is returned.
*
* @param n The size of the tuples to create
* @example
*
* [1, 2, 3, 4, 5].aperture(2) //=> [[1, 2], [2, 3], [3, 4], [4, 5]]
* [1, 2, 3, 4, 5].aperture(3) //=> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
* [1, 2, 3, 4, 5].aperture(7) //=> []
*/
aperture(n: number): T[][]
/**
* Returns a new list containing the contents of current list, followed by
* the given element.
*
* @param elem The element to add to the end of the new list.
* @example
*
* ['write', 'more'].appending('tests') //=> ['write', 'more', 'tests']
* [].appending('tests') //=> ['tests']
* ['write', 'more'].appending(['tests']) //=> ['write', 'more', ['tests']]
*/
appending(elem: T): T[]
/**
* Returns the result of concatenating the current list with the given lists.
*
* @param other The second list
* @example
*
* [4, 5, 6].appendingAll([1, 2, 3]) //=> [4, 5, 6, 1, 2, 3]
* [].appendingAll([]) //=> []
*/
appendingAll(other: T[]): T[]
/**
* `chain` maps a function over the current list and concatenates the results.
* `chain` is also known as `flatMap` in some libraries.
*
* @param fn The function to map with
* @example
*
* const duplicate = n => [n, n]
* [1, 2, 3].chain(duplicate) //=> [1, 1, 2, 2, 3, 3]
*/
chain<R>(fn: (value: T) => R[]): R[]
/**
* Returns all but the first `n` elements.
*
* @param n
* @example
*
* ['foo', 'bar', 'baz'].drop(1) //=> ['bar', 'baz']
* ['foo', 'bar', 'baz'].drop(2) //=> ['baz']
* ['foo', 'bar', 'baz'].drop(3) //=> []
* ['foo', 'bar', 'baz'].drop(4) //=> []
*/
drop(n: number): T[]
/**
* Returns all but the last `n` elements.
*
* @param {Number} n
* @example
*
* ['foo', 'bar', 'baz'].drop(1) //=> ['foo', 'bar']
* ['foo', 'bar', 'baz'].drop(2) //=> ['foo']
* ['foo', 'bar', 'baz'].drop(3) //=> []
* ['foo', 'bar', 'baz'].drop(4) //=> []
*/
dropLast(n: number): T[]
/**
* Returns a new list without any consecutively repeating elements.
*
* @example
*
* [1, 1, 1, 2, 3, 4, 4, 2, 2].dropRepeats() //=> [1, 2, 3, 4, 2]
*/
dropRepeats(): T[]
/**
* Checks if list ends with the provided sublist.
*
* @param {*} suffix
* @example
*
* ['a', 'b', 'c'].endsWith(['c']) //=> true
* ['a', 'b', 'c'].endsWith('c') //=> true
* ['a', 'b', 'c'].endsWith(['b']) //=> false
* ['a', 'b', 'c'].endsWith('b') //=> false
*/
endsWith(suffix: T | T[]): boolean
/**
* Returns the first element of the list which matches the predicate, or
* Error if no element matches.
*
* @param fn The predicate function used to determine if the element is the
* desired one.
* @example
*
* const xs = [{a: 1}, {a: 2}, {a: 3}]
* xs.find(R.propEq('a', 2)) //=> Ok {a: 2}
* xs.find(R.propEq('b', 2)) //=> Error
*/
tryFind(fn: (elem: T) => boolean): Optional<T>
/**
* Returns the last element of the list which matches the predicate, or
* Error if no element matches.
*
* @param fn The predicate function used to determine if the element is the
* desired one.
* @example
*
* const xs = [{a: 1}, {a: 2}, {a: 2}]
* xs.find(R.propEq('a', 2)) //=> Ok {a: 2}
* xs.find(R.propEq('b', 2)) //=> Error
*/
tryFindLast(fn: (elem: T) => boolean): Optional<T>
/**
* Returns a promise of a filtered list containing only the elements for
* which the provided function yields true.
*
* @param fn Asynchronous filter function
* @example
*
* ['/root/test.txt', '/root/test2.txt'].filter(x => fs.pathExists(x)) //=> Promise ['/root/test.txt']
*/
filterAsync(fn: (elem: T) => Promise<boolean>): Promise<T[]>
/**
* Run asynchronous action for each element in list in sequents and not in parallel.
* @param fn Asynchronous action to run for each element
* @example
* [1, 2, 3].forEachSync(async elem =>
* const res = await addOneDelayed(elem)
* console.log(res)
* )
* //=> 2
* //=> 3
* //=> 4
*/
forEachSync(fn: (elem: T, index: number) => Promise<void>): Promise<void>
/**
* Splits a list into sub-lists stored in an object, based on the result of
* calling a String-returning function on each element, and grouping the
* results according to values returned.
*
* @param fn Function :: a -> String
* @example
*
* const students = [
* { name: 'Abby', score: 84 },
* { name: 'Eddy', score: 58 },
* { name: 'Jack', score: 69 }
* ]
*
* students.groupBy(({ score }) =>
* score < 65 ? 'F' :
* score < 70 ? 'D' :
* score < 80 ? 'C' :
* score < 90 ? 'B' : 'A'
* )
* // {
* // 'A': [{name: 'Dianne', score: 99}],
* // 'B': [{name: 'Abby', score: 84}]
* // // ...,
* // 'F': [{name: 'Eddy', score: 58}]
* // }
*/
groupBy(fn: (elem: T) => string): {
[key: string]: T[]
}
/**
* Returns a list of lists where each sublist's elements are all satisfied
* pairwise comparison according to the provided function.
* Only adjacent elements are passed to the comparison function.
*
* @param fn Function for determining whether two given (adjacent)
* elements should be in the same group
* @example
*
* [0, 1, 1, 2, 3, 5, 8, 13, 21].groupWith((a, b) => a + 1 === b)
* //=> [[0, 1], [1, 2, 3], [5], [8], [13], [21]]
*
* [0, 1, 1, 2, 3, 5, 8, 13, 21].groupWith((a, b) => a % 2 === b % 2)
* //=> [[0], [1, 1], [2], [3, 5], [8], [13, 21]]
*/
groupWith(fn: (a: T, b: T) => boolean): T[][]
/**
* Returns `true` if the specified value is equal, in [`R.equals`](#equals)
* terms, to at least one element
`false` otherwise.
*
* @param elem The item to compare against.
* @example
*
* [1, 2, 3].includes(3) //=> true
* [1, 2, 3].includes(4) //=> false
* [{ name: 'Fred' }].includes({ name: 'Fred' }) //=> true
* [[42]].includes([42])
//=> true
*/
includes(elem: T): boolean
/**
* Inserts the supplied element into the list, at the specified `index`. _Note that
* this is not destructive_: it returns a copy of the list with the changes.
* <small>No lists have been harmed in the application of this function.</small>
*
* @param index The position to insert the element
* @param value The element to insert into the Array
* @example
*
* [1, 2, 3, 4].insert(2, 5) //=> [1, 2, 5, 3, 4]
*/
insert(index: number, value: T): T[]
/**
* Inserts the sub-list into the list, at the specified `index`. _Note that this is not
* destructive_: it returns a copy of the list with the changes.
* <small>No lists have been harmed in the application of this function.</small>
*
* @param index The position to insert the sub-list
* @param values The sub-list to insert into the Array
* @example
*
* [1, 2, 3, 4].insertAll(2, [7, 8, 9]) //=> [1, 2, 7, 8, 9, 3, 4]
*/
insertAll(index: number, values: T[]): T[]
/**
* Creates a new list with the separator interposed between elements.
*
* @param separator The element to add to the list.
* @example
*
* ['b', 'n', 'n', 's'].intersperse('a') //=> ['b', 'a', 'n', 'a', 'n', 'a', 's']
*/
intersperse(elem: T): T[]
/**
* Apply function to each element of the list and remove resulting empty
* values (undefined, null, '', [], Error, None).
*
* @param {Function} fn The function to be called on every element of the input `list`.
* @example
*
* [1, 2, 3].mapNonEmpty(x => x === 2 ? undefined : x + 1) //=> [2, 4]
*/
mapNonEmpty<R>(f: (elem: T, index: number) => Result<R> | Optional<R> | R): R[]
/**
* Apply asynchronous function to each element of the list and remove resulting empty
* values (undefined, null, '', [], Error, None).
*
* @param {Function} fn The asynchronous function to be called on every element of the input `list`.
* @example
*
* [1, 2, 3].mapAsyncNonEmpty(x => x === 2 ? Promise.resolve(undefined) : addOneDelayed(x)) //=> Promise [2, 4]
*/
mapAsyncNonEmpty<R>(f: (elem: T, index: number) => Promise<Result<R> | Optional<R> | R>): Promise<R[]>
/**
* Apply asynchronous function to each element of the list and return a list promise.
*
* @param {Function} fn The asynchronous function to be called on every element of the input `list`.
* @example
*
* [1, 2, 3].mapAsync(x => addOneDelayed(x)) //=> Promise [2, 3, 4]
*/
mapAsync<R>(f: (elem: T, index: number) => Promise<R>): Promise<R[]>
/**
* Apply asynchronous function to each element of the list in sequence and return a list promise.
*
* @param {Function} fn The asynchronous function to be called on every element of the input `list`.
* @example
*
* [1, 2, 3].mapSync(x => addOneDelayed(x)) //=> Promise [2, 3, 4]
*/
mapSync<R>(nf: (elem: T, index: number) => Promise<R>): Promise<R[]>
/**
* Returns the nth element of the list. If n is negative the element at
* index length + n is returned.
*
* @param offset
* @example
*
* const list = ['foo', 'bar', 'baz', 'quux']
* list.nth(1) //=> Some 'bar'
* list.nth(-1) //=> Some 'quux'
* list.nth(-99) //=> None
*/
nth(offset: number): Optional<T>
/**
* Takes a predicate and returns the pair of the same type of elements which do and do not
* satisfy, the predicate, respectively.
*
* @param fn A predicate to determine which side the element belongs to.
* @example
*
* ['sss', 'ttt', 'foo', 'bars'].partition(x => x.includes('s'))
* // => [ [ 'sss', 'bars' ], [ 'ttt', 'foo' ] ]
*/
partition(fn: (elem: T) => boolean): [T[], T[]]
/**
* Returns a new list with the given element at the front, followed by the
* contents of the list.
*
* @param value The item to add to the head of the output list.
* @example
*
* ['fi', 'fo', 'fum'].prepend('fee') //=> ['fee', 'fi', 'fo', 'fum']
*/
prepend(value: T): T[]
/**
* The complement of filter.
*
* @param fn Filter function
* @example
*
* [1, 2, 3, 4].reject(n => n % 2 === 1) //=> [2, 4]
*/
reject(fn: (elem: T) => boolean): T[]
/**
* Removes element from list.
*
* @param elem Element to remove
* @example
*
* [1, 2, 3, 4, 5, 2].remove(2) //=> [1, 3, 4, 5]
*/
remove(elem: T): T[]
/**
* Splits list at a given index.
*
* @param index The index where the list is split.
* @example
*
* [1, 2, 3].splitAt(1) //=> [[1], [2, 3]]
*/
splitAt(index: number): T[][]
/**
* Splits list into slices of the specified length.
*
* @param n Slice length
* @example
*
* [1, 2, 3, 4, 5, 6, 7].splitEvery(3) //=> [[1, 2, 3], [4, 5, 6], [7]]
*/
splitEvery(n: number): T[][]
/**
* Splits list into `n` slices.
*
* @param n Slice length
* @example
*
* [1, 2, 3, 4, 5, 6, 7].splitInto(2) //=> [ [ 1, 2, 3, 4 ], [ 5, 6, 7 ] ]
*/
splitInto(n: number): T[][]
/**
* Returns a pair of lists with the following properties:
*
* - the result of concatenating the two output lists is equivalent to the input list
* - none of the elements of the first output list satisfies the predicate
and
* - if the second output list is non-empty, its first element satisfies the predicate.
*
* @param fn The predicate that determines where the array is split.
* @param {Array} list The array to be split.
* @return {Array}
* @example
*
* [1, 2, 3, 1, 2, 3].splitWhen(x => x === 2) //=> [[1], [2, 3, 1, 2, 3]]
*/
splitWhen(fn: (elem: T) => boolean): T[][]
/**
* Checks if list starts with the provided sublist.
*
* @param prefix
* @example
*
* ['a', 'b', 'c'].startsWith(['a']) //=> true
* ['a', 'b', 'c'].startsWith(['b']) //=> false
*/
startsWith(prefix: T | T[]): boolean
/**
* Returns a new list containing the first `n` elements.
*
* @param n The number of elements to return.
* @example
*
* ['foo', 'bar', 'baz'].take(1) //=> ['foo']
* ['foo', 'bar', 'baz'].take(2) //=> ['foo', 'bar']
* ['foo', 'bar', 'baz'].take(3) //=> ['foo', 'bar', 'baz']
* ['foo', 'bar', 'baz'].take(4) //=> ['foo', 'bar', 'baz']
*/
take(n: number): T[]
/**
* Returns a new list containing the last `n` elements.
*
* @param n The number of elements to return.
* @example
*
* ['foo', 'bar', 'baz'].takeLast(1) //=> ['baz']
* ['foo', 'bar', 'baz'].takeLast(2) //=> ['bar', 'baz']
* ['foo', 'bar', 'baz'].takeLast(3) //=> ['foo', 'bar', 'baz']
* ['foo', 'bar', 'baz'].takeLast(4) //=> ['foo', 'bar', 'baz']
*/
takeLast(n: number): T[]
/**
* Returns a new list containing the first `n` elements of the list,
* passing each value to the supplied predicate function, and terminating when
* the predicate function returns `false`. Excludes the element that caused the
* predicate function to fail.
*
* @param fn The function called per iteration.
* @example
*
* [1, 2, 3, 4, 3, 2, 1].takeWhile(x => x !== 4) //=> [1, 2, 3]
*/
takeWhile(fn: (elem: T) => boolean): T[]
/**
* Returns a new list containing the last `n` elements of the list, passing
* each value to the supplied predicate function, and terminating when the
* predicate function returns `false`. Excludes the element that caused the
* predicate function to fail.
*
* @param fn The function called per iteration.
* @example
*
* [1, 2, 3, 4].takeLastWhile(x => x !== 1)
//=> [2, 3, 4]
*/
takeLastWhile(fn: (elem: T) => boolean): T[]
/**
* Combines current lists with other list into a set (i.e. no duplicates) composed
* of the elements of each list.
*
* @param other The second list.
* @example
*
* [1, 2, 3].union([2, 3, 4]) //=> [1, 2, 3, 4]
*/
union(other: T[]): T[]
/**
* Returns a new list containing only one copy of each element.
*
* @example
*
* [1, 1, 2, 1].unique() //=> [1, 2]
* [1, '1'].unique() //=> [1, '1']
* [[42], [42]].unique() //=> [[42]]
*/
unique(): T[]
/**
* Returns a new list containing only one copy of each element, based upon
* the value returned by applying the supplied function to each list element.
* Prefers the first item if the supplied function produces the same value on
* two items.
*
* @param fn A function used to produce a value to use during comparisons.
* @example
*
* [-1, -5, 2, 10, 1, 2].uniqueBy(Math.abs) //=> [-1, -5, 2, 10]
*/
uniqueBy<R>(fn: (elem: T) => R): T[]
/**
* Returns a new copy of the list with the element at the provided index
* replaced with the given value.
*
* @param index The index to update.
* @param value The value to exist at the given index of the returned array.
* @example
*
* ['a', 'b', 'c'].update(1, '_') //=> ['a', '_', 'c']
* ['a', 'b', 'c'].update(-1, '_') //=> ['a', 'b', '_']
*/
update(index: number, value: T): T[]
/**
* Returns a new list without values in the first argument.
*
* @param values The values to be removed.
* @example
*
* [1, 2, 1, 3, 4].without([1, 2]) //=> [3, 4]
*/
without(values: T[]): T[]
/**
* Returns a new list without `empty` values (undefined, null, '', [], Error, None).
*
* @example
*
* [1, null, undefined, 3, [], Optional.none()].withoutEmpty() //=> [1, 3]
*/
withoutEmpty(): T[]
/**
* Creates a new list out of this and a supplied list by creating each
* possible pair from the lists.
*
* @param other Other list.
* @example
*
* [1, 2].xprod(['a', 'b']) //=> [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
*/
xprod<R>(other: R[]): [T, R][]
/**
* Creates a new list out of this and a supplied list by pairing up equally-positioned
* items from both lists. The returned list is truncated to the length of the
* shorter of the two input lists.
*
* @param other The second array to consider.
* @example
*
* [1, 2, 3].zip(['a', 'b', 'c']) //=> [[1, 'a'], [2, 'b'], [3, 'c']]
*/
zip<S>(other: S[]): [T, S][]
/**
* Creates a new list out of this and a supplied list by applying the function to each
* equally-positioned pair in the lists. The returned list is truncated to the
* length of the shorter of the two input lists.
*
* @param other The second array to consider.
* @param fn The function used to combine the two elements into one value.
* @example
*
* const f = (x, y) => //...
* [1, 2, 3].zipWith(['a', 'b', 'c'], f)
* //=> [f(1, 'a'), f(2, 'b'), f(3, 'c')]
*/
zipWith<S, R>(other: S[], fn: (a: T, b: S) => R): R[]
/**
* Creates a new list out of this by pairing up the items with their index.
*
* @param other The second array to consider.
* @example
*
* ['a', 'b', 'c'].zipWithIndex() //=> [[0, 'a'], [1, 'b'], [2, 'c']]
*/
zipWithIndex(): [number, T][]
}