🐆 An immutable list with unmatched performance and a comprehensive functional API.
TypeScript
1,649
483 commits
updated Jan 15, 2024
A fast immutable list with a functional API.
List is a purely functional alternative to arrays. It is an
implementation of a fast persistent sequence data structure. Compared
to JavaScript's Array List has three major benefits.
Array
and a lot of additional functions that'll save the day once you need them.list/methods to get chainable methods or
alterntively import list/curried to get a version of the API where every
function is curried. Both variants are 100% TypeScript compatible.import * as L from "list" in itself adds
zero bytes to your bundle when using Webpack. Using a function adds only that
function and the very small (<1KB) core of the library. You only pay in size
for the functions that you actually use.for..of loops, works with destructuring, and
can be passed to any function expecting an iterable. See more.This section explains how to get started using List. First you'll have to install the library.
npm i list
Then you can import it.
// As an ES module
import * as L from "list";
// Or with require
const L = require("list");
Then you can begin using List instead of arrays and enjoy immutability the performance benefits.
As a replacement for array literals List offers the function list
for constructing lists. Instead of using [...] to construct an array
with the content ... one can use list(...) to construct a list
with the same content. Here is an example.
// An array literal
const myArray = [0, 1, 2, 3];
// The List equivalent
const myList = L.list(0, 1, 2, 3);
List has all the common functions that you know from native arrays and other libraries.
const myList = L.list(0, 1, 2, 3, 4, 5);
myList.length; //=> 6
L.filter(isEven, myList); //=> list(0, 2, 4)
L.map(n => n * n, myList); //=> list(0, 1, 4, 9, 16, 25)
L.reduce((sum, n) => sum + n, 0, myList); //=> 15
L.slice(2, 5, myList); //=> list(2, 3, 4)
L.concat(myList, L.list(6, 7, 8)); //=> list(0, 1, 2, 3, 4, 5, 6, 7, 8);
You'll probably also end up needing to convert between arrays and
List. You can do that with the functions from and toArray.
L.toArray(L.list("foo", "bar")); //=> ["foo", "bar"];
L.from(["foo", "bar"]); //=> L.list("foo", "bar");
List offers a wealth of other useful and high-performing functions. You can see them all in the API documentation
List offers several API styles. By default the library exports "plain"
functions. Additionally curried functions can be imported from list/curried
and an API with chainable methods can be imported from list/methods. The
differences are illustrated below.
The default export offers normal plain function.
import * as L from "list";
const l = L.take(5, L.sortBy(p => p.name, L.filter(p => p.age > 22, people)));
In list/methods all functions are available as chainable methods.
import * as L from "list/methods";
const l = people
.filter(p => p.age > 22)
.sortBy(p => p.name)
.take(5);
In list/curried all functions are curried. In the example below the partially
applied functions are composed together using Ramda's
pipe. Alternatively one could have used
Lodash's flowRight.
import * as R from "ramda";
import * as L from "list/curried";
const l = R.pipe(L.filter(p => p.age > 22), L.sortBy(p => p.name), L.take(5))(
people
);
List implements the JavaScript iterable protocol. This means that lists can be used with array destructuring just like normal arrays.
const myList = L.list("first", "second", "third", "fourth");
const [first, second] = myList;
first; //=> "first"
second; //=> "second"
Lists can also be used in for..of loops.
for (const element of myList) {
console.log(element);
}
// logs: first, second, third, fourth
And they can be passed to any function that takes an iterable as its argument.
As an example a list can be converted into a native
Set.
const mySet = new Set(myList);
mySet.has("third"); //=> true
This works because the Set constructor accepts any iterable as
argument.
Lists also work with spread syntax. For instance, you can call a function like this.
console.log(...list("hello", "there", "i'm", "logging", "elements"));
Then each element of the list will be passed as an argument to console.log.
List also suports iterating backwards over lists through the
backwards function.
The iterable protocol allows for some very convenient patterns and means that lists can integrate nicely with JavaScript syntax. But, here are two anti-patterns that you should be aware of.
for..of loops. Functions like map and
foldl are often a better choice. If you want to perform
a side-effect for each element in a list you should probably use
forEach.const [a, b, ...cs] = myList; // Don't do this
The syntax converts the rest of the iterable (in this case a list)
into an array by iterating through the entire iterable. This is
slow and it turns our list into an array. This alternative avoids
both problems.
const [[a, b], cs] = splitAt(2, myList); // Do this
This uses the splitAt function which splits and
creates the list cs very efficiently in O(log(n)) time.List is designed to work seamlessly together with Ramda. Ramda offers a large number of useful functions for working with arrays. List implements the same functions on its immutable data structure. This means that Ramda users can keep using the API they're familiar with. Additionally, List offers an entry point where all functions are curried.
Since List implements Ramda's array API it is very easy to convert code from using arrays to using immutable lists. As an example, consider the code below.
import * as R from "ramda";
R.pipe(R.filter(n => n % 2 === 0), R.map(R.multiply(3)), R.reduce(R.add, 0))(
array
);
The example can be converted to code using List as follows.
import * as R from "ramda";
import * as L from "list/curried";
R.pipe(L.filter(n => n % 2 === 0), L.map(R.multiply(3)), L.reduce(R.add, 0))(
list
);
For each function operating on arrays, the R is simply changed to an L.
This works because List exports functions that have the same names and behavior
as Ramdas functions.
The goal is to implement the entirety of Ramda's array functions for List. The list below keeps track of how many of Ramda functions that are missing and of how many that are already implemented. Currently 61 out of 76 functions have been implemented.
Implemented: adjust, all, any, append, chain, concat, contains,
drop, dropLast, dropRepeats, dropRepeatsWith, dropWhile, filter,
find, findIndex, findLast, group, groupWith, head, flatten,
indexOf, intersperse, init, insert, insertAll, last, lastIndexOf,
length, join, map, none, nth, pair, partition, pluck,
prepend, range, reduce, reduceRight, reduceWhile, reject, remove,
reverse, repeat, scan, sequence, slice, sort, splitAt,
splitEvery, splitWhen, take, takeWhile, tail,
takeLast,takeLastWhile, traverse, times, update, zip, zipWith.
Not implemented: aperture, dropLastWhile, endsWith, findLastIndex,
indexBy, mapAccum, mapAccumRight, startsWith, transpose, unfold,
uniq, uniqBy, uniqWith, unnest without, xprod.
While List tries to stay as close to Ramda's API as possible there are a few deviations to be aware of.
R.__ placeholder. Instead of
R.reduce(R.__, 0, l) one alternative is to use an arrow function _ => L.reduce(_, 0, l) instead.sort and sortWith are different compared to what
they do in Ramda. L.sortWith is equivalent to R.sort and L.sort sorts a
list without taking a comparison function. This makes the common case of
sorting a list of numbers or strings easier
List currently implements the following Fantasy Land and Static Land specifications: Setoid, semigroup, monoid, foldable, functor, apply, applicative, chain, monad.
The following specifications have not been implemented yet: Traversable, Ord.
Since methods hinder tree-shaking the Fantasy Land methods are not included by default. In order to get them you must import it likes this:
import "list/fantasy-land";
The API is organized into three parts.
listCreates a list based on the arguments given.
Complexity: O(n)
Example
const l = list(1, 2, 3, 4); // creates a list of four elements
const l2 = list("foo"); // creates a singleton
emptyReturns an empty list.
Complexity: O(1)
Example
const emptyList = empty(); //=> list()
ofTakes a single arguments and returns a singleton list that contains it.
Complexity: O(1)
Example
of("foo"); //=> list("foo")
pairTakes two arguments and returns a list that contains them.
Complexity: O(1)
Example
pair("foo", "bar"); //=> list("foo", "bar")
fromConverts an array, an array-like or an itearble into a list.
Complexity: O(n)
Example
from([0, 1, 2, 3, 4]); //=> list(0, 1, 2, 3, 4)
rangeReturns a list of numbers between an inclusive lower bound and an exclusive upper bound.
Complexity: O(n)
Example
range(3, 8); //=> list(3, 4, 5, 6, 7)
repeatReturns a list of a given length that contains the specified value in all positions.
Complexity: O(n)
Example
repeat(1, 7); //=> list(1, 1, 1, 1, 1, 1, 1)
repeat("foo", 3); //=> list("foo", "foo", "foo")
timesReturns a list of given length that contains the value of the given function called with current index.
Complexity: O(n)
Example
const twoFirsOdds = times(i => i * 2 + 1, 2);
const dots = times(() => {
const x = Math.random() * width;
const y = Math.random() * height;
return { x, y };
}, 50);
concatConcatenates two lists.
Complexity: O(log(n))
Example
concat(list(0, 1, 2), list(3, 4)); //=> list(0, 1, 2, 3, 4)
flattenFlattens a list of lists into a list. Note that this function does not flatten recursively. It removes one level of nesting only.
Complexity: O(n * log(m)) where n is the length of the outer
list and m the length of the inner lists.
Example
const nested = list(list(0, 1, 2, 3), list(4), empty(), list(5, 6));
flatten(nested); //=> list(0, 1, 2, 3, 4, 5, 6)
prependPrepends an element to the front of a list and returns the new list.
Complexity: O(log(n)), practically constant
Example
const newList = prepend(0, list(1, 2, 3)); //=> list(0, 1, 2, 3)
appendAppends an element to the end of a list and returns the new list.
Complexity: O(log(n)), practically constant
Example
const newList = append(3, list(0, 1, 2)); //=> list(0, 1, 2, 3)
intersperseInserts a separator between each element in a list.
Example
intersperse("n", list("ba", "a", "a")); //=> list("ba", "n", "a", "n", "a")
mapApplies a function to each element in the given list and returns a new list of the values that the function return.
Complexity: O(n)
Example
map(n => n * n, list(0, 1, 2, 3, 4)); //=> list(0, 1, 4, 9, 16)
pluckExtracts the specified property from each object in the list.
Example
const l = list(
{ foo: 0, bar: "a" },
{ foo: 1, bar: "b" },
{ foo: 2, bar: "c" }
);
pluck("foo", l); //=> list(0, 1, 2)
updateReturns a list that has the entry specified by the index replaced with the given value.
If the index is out of bounds the given list is returned unchanged.
Complexity: O(log(n))
Example
update(2, "X", list("a", "b", "c", "d", "e")); //=> list("a", "b", "X", "d", "e")
adjustReturns a list that has the entry specified by the index replaced with the value returned by applying the function to the value.
If the index is out of bounds the given list is returned unchanged.
Complexity: O(log(n))
Example
adjust(2, inc, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 3, 3, 4, 5)
sliceReturns a slice of a list. Elements are removed from the beginning and end. Both the indices can be negative in which case they will count from the right end of the list.
Complexity: O(log(n))
Example
const l = list(0, 1, 2, 3, 4, 5);
slice(1, 4, l); //=> list(1, 2, 3)
slice(2, -2, l); //=> list(2, 3)
takeTakes the first n elements from a list and returns them in a new list.
Complexity: O(log(n))
Example
take(3, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 2)
takeWhileTakes the first elements in the list for which the predicate returns
true.
Complexity: O(k + log(n)) where k is the number of elements
satisfying the predicate.
Example
takeWhile(n => n < 4, list(0, 1, 2, 3, 4, 5, 6)); //=> list(0, 1, 2, 3)
takeWhile(_ => false, list(0, 1, 2, 3, 4, 5)); //=> list()
takeLastTakes the last n elements from a list and returns them in a new
list.
Complexity: O(log(n))
Example
takeLast(3, list(0, 1, 2, 3, 4, 5)); //=> list(3, 4, 5)
takeLastWhileTakes the last elements in the list for which the predicate returns
true.
Complexity: O(k + log(n)) where k is the number of elements
satisfying the predicate.
Example
takeLastWhile(n => n > 2, list(0, 1, 2, 3, 4, 5)); //=> list(3, 4, 5)
takeLastWhile(_ => false, list(0, 1, 2, 3, 4, 5)); //=> list()
splitAtSplits a list at the given index and return the two sides in a pair. The left side will contain all elements before but not including the element at the given index. The right side contains the element at the index and all elements after it.
Complexity: O(log(n))
Example
const l = list(0, 1, 2, 3, 4, 5, 6, 7, 8);
splitAt(4, l); //=> [list(0, 1, 2, 3), list(4, 5, 6, 7, 8)]
splitWhenSplits a list at the first element in the list for which the given
predicate returns true.
Complexity: O(n)
Example
const l = list(0, 1, 2, 3, 4, 5, 6, 7);
splitWhen((n) => n > 3, l); //=> [list(0, 1, 2, 3), list(4, 5, 6, 7)]
removeTakes an index, a number of elements to remove and a list. Returns a new list with the given amount of elements removed from the specified index.
Complexity: O(log(n))
Example
const l = list(0, 1, 2, 3, 4, 5, 6, 7, 8);
remove(4, 3, l); //=> list(0, 1, 2, 3, 7, 8)
remove(2, 5, l); //=> list(0, 1, 7, 8)
dropReturns a new list without the first n elements.
Complexity: O(log(n))
Example
drop(2, list(0, 1, 2, 3, 4, 5)); //=> list(2, 3, 4, 5)
dropWhileRemoves the first elements in the list for which the predicate returns
true.
Complexity: O(k + log(n)) where k is the number of elements
satisfying the predicate.
Example
dropWhile(n => n < 4, list(0, 1, 2, 3, 4, 5, 6)); //=> list(4, 5, 6)
dropLastReturns a new list without the last n elements.
Complexity: O(log(n))
Example
dropLast(2, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 2, 3)
dropRepeatsReturns a new list without repeated elements.
Complexity: O(n)
Example
dropRepeats(L.list(0, 0, 1, 1, 1, 2, 3, 3, 4, 4)); //=> list(0, 1, 2, 3, 4)
dropRepeatsWithReturns a new list without repeated elements by using the given function to determine when elements are equal.
Complexity: O(n)
Example
dropRepeatsWith(
(n, m) => Math.floor(n) === Math.floor(m),
list(0, 0.4, 1.2, 1.1, 1.8, 2.2, 3.8, 3.4, 4.7, 4.2)
); //=> list(0, 1, 2, 3, 4)
tailReturns a new list with the first element removed. If the list is empty the empty list is returne.
Complexity: O(1)
Example
tail(list(0, 1, 2, 3)); //=> list(1, 2, 3)
tail(empty()); //=> list()
popReturns a new list with the last element removed. If the list is empty the empty list is returned.
Aliases: init
Complexity: O(1)
Example
pop(list(0, 1, 2, 3)); //=> list(0, 1, 2)
filterReturns a new list that only contains the elements of the original
list for which the predicate returns true.
Complexity: O(n)
Example
filter(isEven, list(0, 1, 2, 3, 4, 5, 6)); //=> list(0, 2, 4, 6)
rejectReturns a new list that only contains the elements of the original
list for which the predicate returns false.
Complexity: O(n)
Example
reject(isEven, list(0, 1, 2, 3, 4, 5, 6)); //=> list(1, 3, 5)
reverseReverses a list.
Complexity: O(n)
Example
reverse(list(0, 1, 2, 3, 4, 5)); //=> list(5, 4, 3, 2, 1, 0)
apApplies a list of functions to a list of values.
Example
ap(list((n: number) => n + 2, n => 2 * n, n => n * n), list(1, 2, 3)); //=> list(3, 4, 5, 2, 4, 6, 1, 4, 9)
chainMaps a function over a list and concatenates all the resulting lists together.
Aliases: flatMap
Example
chain(n => list(n, 2 * n, n * n), list(1, 2, 3)); //=> list(1, 2, 1, 2, 4, 4, 3, 6, 9)
partitionSplits the list into two lists. One list that contains all the values
for which the predicate returns true and one containing the values for
which it returns false.
Complexity: O(n)
Example
partition(isEven, list(0, 1, 2, 3, 4, 5)); //=> list(list(0, 2, 4), list(1, 3, 5))
insertInserts the given element at the given index in the list.
Complexity: O(log(n))
Example
insert(2, "c", list("a", "b", "d", "e")); //=> list("a", "b", "c", "d", "e")
insertAllInserts the given list of elements at the given index in the list.
Complexity: O(log(n))
Example
insertAll(2, list("c", "d"), list("a", "b", "e", "f")); //=> list("a", "b", "c", "d", "e", "f")
zipWithThis is like mapping over two lists at the same time. The two lists are iterated over in parallel and each pair of elements is passed to the function. The returned values are assembled into a new list.
The shortest list determine the size of the result.
Complexity: O(log(n)) where n is the length of the smallest list.
Example
const names = list("Turing", "Curry");
const years = list(1912, 1900);
zipWith((name, year) => ({ name, year }), names, years);
//=> list({ name: "Turing", year: 1912 }, { name: "Curry", year: 1900 });
zipIterate over two lists in parallel and collect the pairs.
Complexity: O(log(n)) where n is the length of the smallest list.
Example
const names = list("a", "b", "c", "d", "e");
const years = list(0, 1, 2, 3, 4, 5, 6);
//=> list(["a", 0], ["b", 1], ["c", 2], ["d", 3], ["e", 4]);
sortSorts the given list. The list should contain values that can be compared using
the < operator or values that implement the Fantasy Land
Ord specification.
Performs a stable sort.
Complexity: O(n * log(n))
Example
sort(list(5, 3, 1, 8, 2)); //=> list(1, 2, 3, 5, 8)
sort(list("e", "a", "c", "b", "d"); //=> list("a", "b", "c", "d", "e")
sortBySort the given list by passing each value through the function and comparing
the resulting value. The function should either return values comparable using
< or values that implement the Fantasy Land
Ord specification.
Performs a stable sort.
Complexity: O(n * log(n))
Example
sortBy(
o => o.n,
list({ n: 4, m: "foo" }, { n: 3, m: "bar" }, { n: 1, m: "baz" })
);
//=> list({ n: 1, m: "baz" }, { n: 3, m: "bar" }, { n: 4, m: "foo" })
sortBy(s => s.length, list("foo", "bar", "ba", "aa", "list", "z"));
//=> list("z", "ba", "aa", "foo", "bar", "list")
sortWithSort the given list by comparing values using the given function. The function
receieves two values and should return -1 if the first value is stricty
larger than the second, 0 is they are equal and 1 if the first values is
strictly smaller than the second.
Note that the comparison function is equivalent to the one required by
Array.prototype.sort.
Performs a stable sort.
Complexity: O(n * log(n))
Example
sortWith((a, b) => {
if (a === b) {
return 0;
} else if (a < b) {
return -1;
} else {
return 1;
}
}, list(5, 3, 1, 8, 2)); //=> list(1, 2, 3, 5, 8)
groupReturns a list of lists where each sublist's elements are all equal.
Complexity: O(n)
Example
group(list(0, 0, 1, 2, 2, 2, 3, 3)); //=> list(list(0, 0), list(1), list(2, 2, 2), list(3, 3))
groupWithReturns a list of lists where each sublist's elements are pairwise equal based on the given comparison function.
Note that only adjacent elements are compared for equality. If all equal elements should be grouped together the list should be sorted before grouping.
Complexity: O(n)
Example
const floorEqual = (a, b) => Math.round(a) === Math.round(b);
groupWith(floorEqual, list(1.1, 1.3, 1.8, 2, 2.2, 3.3, 3.4));
//=> list(list(1.1, 1.3), list(1.8, 2, 2.2), list(3.3, 3.4))
const sameLength = (a, b) => a.length === b.length;
groupWith(sameLength, list("foo", "bar", "ab", "bc", "baz"));
//=> list(list("foo", "bar"), list("ab", "bc"), list("baz))
isListReturns true if the given argument is a list.
Complexity: O(1)
Example
isList([0, 1, 2]); //=> false
isList("string"); //=> false
isList({ foo: 0, bar: 1 }); //=> false
isList(list(0, 1, 2)); //=> true
isEmptyReturns true if the list is empty.
Complexity: O(1)
Example
isEmpty(empty()); //=> true
isEmpty(list()); //=> true
isEmpty(list(0, 1, 2)); //=> false
equalsReturns true if the two lists are equivalent.
Complexity: O(n)
Example
equals(list(0, 1, 2, 3), list(0, 1, 2, 3)); //=> true
equals(list("a", "b", "c"), list("a", "z", "c")); //=> false
equalsWithReturns true if the two lists are equivalent when comparing each
pair of elements with the given comparison function.
Complexity: O(n)
Example
equalsWith(
(n, m) => n.length === m.length,
list("foo", "hello", "one"),
list("bar", "world", "two")
); //=> true
toArrayConverts a list into an array.
Complexity: O(n)
Example
toArray(list(0, 1, 2, 3, 4)); //=> [0, 1, 2, 3, 4]
backwardsReturns an iterable that iterates backwards over the given list.
Complexity: O(1)
Example
const l = list(0, 1, 2, 3, 4)
for (const n of backwards(l)) {
if (l < 2) {
break;
}
console.log(l);
}
// => logs 4, 3, and then 2
nthGets the nth element of the list. If n is out of bounds
undefined is returned.
Complexity: O(log(n)), practically constant
Example
const l = list(0, 1, 2, 3, 4);
nth(2, l); //=> 2
lengthReturns the length of a list. I.e. the number of elements that it contains.
Complexity: O(1)
Example
length(list(0, 1, 2, 3)); //=> 4
firstReturns the first element of the list. If the list is empty the
function returns undefined.
Aliases: head
Complexity: O(1)
Example
first(list(0, 1, 2, 3)); //=> 0
first(list()); //=> undefined
lastReturns the last element of the list. If the list is empty the
function returns undefined.
Complexity: O(1)
Example
last(list(0, 1, 2, 3)); //=> 3
last(list()); //=> undefined
foldlFolds a function over a list. Left-associative.
Aliases: reduce
Complexity: O(n)
Example
foldl((n, m) => n - m, 1, list(2, 3, 4, 5));
1 - 2 - 3 - 4 - 5; //=> -13
foldrFolds a function over a list. Right-associative.
Aliases: reduceRight
Complexity: O(n)
Example
foldr((n, m) => n - m, 5, list(1, 2, 3, 4));
1 - (2 - (3 - (4 - 5))); //=> 3
foldlWhileSimilar to foldl. But, for each element it calls the predicate function
before the folding function and stops folding if it returns false.
Aliases: reduceWhile
Complexity: O(n)
Example
const isOdd = (_acc:, x) => x % 2 === 1;
const xs = L.list(1, 3, 5, 60, 777, 800);
foldlWhile(isOdd, (n, m) => n + m, 0, xs) //=> 9
const ys = L.list(2, 4, 6);
foldlWhile(isOdd, (n, m) => n + m, 111, ys) //=> 111
scanFolds a function over a list from left to right while collecting all the intermediate steps in a resulting list.
Complexity: O(n)
Example
const l = list(1, 3, 5, 4, 2);
L.scan((n, m) => n + m, 0, l); //=> list(0, 1, 4, 9, 13, 15));
L.scan((s, m) => s + m.toString(), "", l); //=> list("", "1", "13", "135", "1354", "13542")
traverseMap each element of list to an applicative, evaluate these applicatives from left to right, and collect the results.
This works with Fantasy Land applicatives.
Complexity: O(n)
Example
const safeDiv = n => d => d === 0 ? nothing : just(n / d)
L.traverse(Maybe, safeDiv(10), list(2, 4, 5)); //=> just(list(5, 2.5, 2))
L.traverse(Maybe, safeDiv(10), list(2, 0, 5)); //=> nothing
sequenceEvaluate each applicative in the list from left to right, and collect the results.
Complexity: O(n)
Example
L.sequence(Maybe, list(just(1), just(2), just(3))); //=> just(list(1, 2, 3))
L.sequence(Maybe, list(just(1), just(2), nothing())); //=> nothing
forEachInvokes a given callback for each element in the list from left to
right. Returns undefined.
This function is very similar to map. It should be used instead of
map when the mapping function has side-effects. Whereas map
constructs a new list forEach merely returns undefined. This makes
forEach faster when the new list is unneeded.
Complexity: O(n)
Example
const l = list(0, 1, 2);
forEach(element => console.log(element));
//=> 0
//=> 1
//=> 2
everyReturns true if and only if the predicate function returns true
for all elements in the given list.
Aliases: all
Complexity: O(n)
Example
const isEven = n => n % 2 === 0;
every(isEven, empty()); //=> true
every(isEven, list(2, 4, 6, 8)); //=> true
every(isEven, list(2, 3, 4, 6, 7, 8)); //=> false
every(isEven, list(1, 3, 5, 7)); //=> false
someReturns true if and only if there exists an element in the list for
which the predicate returns true.
Aliases: any
Complexity: O(n)
Example
const isEven = n => n % 2 === 0;
some(isEven, empty()); //=> false
some(isEven, list(2, 4, 6, 8)); //=> true
some(isEven, list(2, 3, 4, 6, 7, 8)); //=> true
some(isEven, list(1, 3, 5, 7)); //=> false
indexOfReturns the index of the first element in the list that is equal to
the given element. If no such element is found -1 is returned.
Complexity: O(n)
Example
const l = list(12, 4, 2, 89, 6, 18, 7);
indexOf(12, l); //=> 0
indexOf(89, l); //=> 3
indexOf(10, l); //=> -1
lastIndexOfReturns the index of the last element in the list that is equal to
the given element. If no such element is found -1 is returned.
Complexity: O(n)
Example
const l = L.list(12, 4, 2, 18, 89, 2, 18, 7);
L.lastIndexOf(18, l); //=> 6
L.lastIndexOf(2, l); //=> 5
L.lastIndexOf(12, l); //=> 0
findReturns the first element for which the predicate returns true. If
no such element is found the function returns undefined.
Complexity: O(n)
Example
find(isEven, list(1, 3, 5, 6, 7, 9, 10)); //=> 6
find(isEven, list(1, 3, 5, 7, 9)); //=> undefined
findLastReturns the last element for which the predicate returns true. If
no such element is found the function returns undefined.
Complexity: O(n)
Example
findLast(isEven, list(1, 3, 5, 6, 7, 8, 9)); //=> 8
findLast(isEven, list(1, 3, 5, 7, 9)); //=> undefined
findIndexReturns the index of the first element for which the predicate returns
true. If no such element is found the function returns -1.
Complexity: O(n)
Example
findIndex(isEven, list(1, 3, 5, 6, 7, 9, 10)); //=> 3
findIndex(isEven, list(1, 3, 5, 7, 9)); //=> -1
noneReturns true if and only if the predicate function returns false
for all elements in the given list.
Complexity: O(n)
Example
const isEven = n => n % 2 === 0;
none(isEven, empty()); //=> true
none(isEven, list(2, 4, 6, 8)); //=> false
none(isEven, list(2, 3, 4, 6, 7, 8)); //=> false
none(isEven, list(1, 3, 5, 7)); //=> true
includesReturns true if the list contains the specified element. Otherwise
it returns false.
Aliases: contains
Complexity: O(n)
Example
includes(3, list(0, 1, 2, 3, 4, 5)); //=> true
includes(3, list(0, 1, 2, 4, 5)); //=> false
joinConcats the strings in a list separated by a specified separator.
Complexity: O(n)
Example
join(", ", list("one", "two", "three")); //=> "one, two, three"
The benchmarks are located in the bench directory.
Overall
Maintained
Code review
npm
Latest release
Releases this year
1,568 followers · starred May 2018
1,522 followers · starred Oct 2019
5,054 followers · starred Jun 2018
515 followers · starred May 2018
🐆 An immutable list with unmatched performance and a comprehensive functional API.
TypeScript
1,649
483 commits
updated Jan 15, 2024
A fast immutable list with a functional API.
List is a purely functional alternative to arrays. It is an
implementation of a fast persistent sequence data structure. Compared
to JavaScript's Array List has three major benefits.
Array
and a lot of additional functions that'll save the day once you need them.list/methods to get chainable methods or
alterntively import list/curried to get a version of the API where every
function is curried. Both variants are 100% TypeScript compatible.import * as L from "list" in itself adds
zero bytes to your bundle when using Webpack. Using a function adds only that
function and the very small (<1KB) core of the library. You only pay in size
for the functions that you actually use.for..of loops, works with destructuring, and
can be passed to any function expecting an iterable. See more.This section explains how to get started using List. First you'll have to install the library.
npm i list
Then you can import it.
// As an ES module
import * as L from "list";
// Or with require
const L = require("list");
Then you can begin using List instead of arrays and enjoy immutability the performance benefits.
As a replacement for array literals List offers the function list
for constructing lists. Instead of using [...] to construct an array
with the content ... one can use list(...) to construct a list
with the same content. Here is an example.
// An array literal
const myArray = [0, 1, 2, 3];
// The List equivalent
const myList = L.list(0, 1, 2, 3);
List has all the common functions that you know from native arrays and other libraries.
const myList = L.list(0, 1, 2, 3, 4, 5);
myList.length; //=> 6
L.filter(isEven, myList); //=> list(0, 2, 4)
L.map(n => n * n, myList); //=> list(0, 1, 4, 9, 16, 25)
L.reduce((sum, n) => sum + n, 0, myList); //=> 15
L.slice(2, 5, myList); //=> list(2, 3, 4)
L.concat(myList, L.list(6, 7, 8)); //=> list(0, 1, 2, 3, 4, 5, 6, 7, 8);
You'll probably also end up needing to convert between arrays and
List. You can do that with the functions from and toArray.
L.toArray(L.list("foo", "bar")); //=> ["foo", "bar"];
L.from(["foo", "bar"]); //=> L.list("foo", "bar");
List offers a wealth of other useful and high-performing functions. You can see them all in the API documentation
List offers several API styles. By default the library exports "plain"
functions. Additionally curried functions can be imported from list/curried
and an API with chainable methods can be imported from list/methods. The
differences are illustrated below.
The default export offers normal plain function.
import * as L from "list";
const l = L.take(5, L.sortBy(p => p.name, L.filter(p => p.age > 22, people)));
In list/methods all functions are available as chainable methods.
import * as L from "list/methods";
const l = people
.filter(p => p.age > 22)
.sortBy(p => p.name)
.take(5);
In list/curried all functions are curried. In the example below the partially
applied functions are composed together using Ramda's
pipe. Alternatively one could have used
Lodash's flowRight.
import * as R from "ramda";
import * as L from "list/curried";
const l = R.pipe(L.filter(p => p.age > 22), L.sortBy(p => p.name), L.take(5))(
people
);
List implements the JavaScript iterable protocol. This means that lists can be used with array destructuring just like normal arrays.
const myList = L.list("first", "second", "third", "fourth");
const [first, second] = myList;
first; //=> "first"
second; //=> "second"
Lists can also be used in for..of loops.
for (const element of myList) {
console.log(element);
}
// logs: first, second, third, fourth
And they can be passed to any function that takes an iterable as its argument.
As an example a list can be converted into a native
Set.
const mySet = new Set(myList);
mySet.has("third"); //=> true
This works because the Set constructor accepts any iterable as
argument.
Lists also work with spread syntax. For instance, you can call a function like this.
console.log(...list("hello", "there", "i'm", "logging", "elements"));
Then each element of the list will be passed as an argument to console.log.
List also suports iterating backwards over lists through the
backwards function.
The iterable protocol allows for some very convenient patterns and means that lists can integrate nicely with JavaScript syntax. But, here are two anti-patterns that you should be aware of.
for..of loops. Functions like map and
foldl are often a better choice. If you want to perform
a side-effect for each element in a list you should probably use
forEach.const [a, b, ...cs] = myList; // Don't do this
The syntax converts the rest of the iterable (in this case a list)
into an array by iterating through the entire iterable. This is
slow and it turns our list into an array. This alternative avoids
both problems.
const [[a, b], cs] = splitAt(2, myList); // Do this
This uses the splitAt function which splits and
creates the list cs very efficiently in O(log(n)) time.List is designed to work seamlessly together with Ramda. Ramda offers a large number of useful functions for working with arrays. List implements the same functions on its immutable data structure. This means that Ramda users can keep using the API they're familiar with. Additionally, List offers an entry point where all functions are curried.
Since List implements Ramda's array API it is very easy to convert code from using arrays to using immutable lists. As an example, consider the code below.
import * as R from "ramda";
R.pipe(R.filter(n => n % 2 === 0), R.map(R.multiply(3)), R.reduce(R.add, 0))(
array
);
The example can be converted to code using List as follows.
import * as R from "ramda";
import * as L from "list/curried";
R.pipe(L.filter(n => n % 2 === 0), L.map(R.multiply(3)), L.reduce(R.add, 0))(
list
);
For each function operating on arrays, the R is simply changed to an L.
This works because List exports functions that have the same names and behavior
as Ramdas functions.
The goal is to implement the entirety of Ramda's array functions for List. The list below keeps track of how many of Ramda functions that are missing and of how many that are already implemented. Currently 61 out of 76 functions have been implemented.
Implemented: adjust, all, any, append, chain, concat, contains,
drop, dropLast, dropRepeats, dropRepeatsWith, dropWhile, filter,
find, findIndex, findLast, group, groupWith, head, flatten,
indexOf, intersperse, init, insert, insertAll, last, lastIndexOf,
length, join, map, none, nth, pair, partition, pluck,
prepend, range, reduce, reduceRight, reduceWhile, reject, remove,
reverse, repeat, scan, sequence, slice, sort, splitAt,
splitEvery, splitWhen, take, takeWhile, tail,
takeLast,takeLastWhile, traverse, times, update, zip, zipWith.
Not implemented: aperture, dropLastWhile, endsWith, findLastIndex,
indexBy, mapAccum, mapAccumRight, startsWith, transpose, unfold,
uniq, uniqBy, uniqWith, unnest without, xprod.
While List tries to stay as close to Ramda's API as possible there are a few deviations to be aware of.
R.__ placeholder. Instead of
R.reduce(R.__, 0, l) one alternative is to use an arrow function _ => L.reduce(_, 0, l) instead.sort and sortWith are different compared to what
they do in Ramda. L.sortWith is equivalent to R.sort and L.sort sorts a
list without taking a comparison function. This makes the common case of
sorting a list of numbers or strings easier
List currently implements the following Fantasy Land and Static Land specifications: Setoid, semigroup, monoid, foldable, functor, apply, applicative, chain, monad.
The following specifications have not been implemented yet: Traversable, Ord.
Since methods hinder tree-shaking the Fantasy Land methods are not included by default. In order to get them you must import it likes this:
import "list/fantasy-land";
The API is organized into three parts.
listCreates a list based on the arguments given.
Complexity: O(n)
Example
const l = list(1, 2, 3, 4); // creates a list of four elements
const l2 = list("foo"); // creates a singleton
emptyReturns an empty list.
Complexity: O(1)
Example
const emptyList = empty(); //=> list()
ofTakes a single arguments and returns a singleton list that contains it.
Complexity: O(1)
Example
of("foo"); //=> list("foo")
pairTakes two arguments and returns a list that contains them.
Complexity: O(1)
Example
pair("foo", "bar"); //=> list("foo", "bar")
fromConverts an array, an array-like or an itearble into a list.
Complexity: O(n)
Example
from([0, 1, 2, 3, 4]); //=> list(0, 1, 2, 3, 4)
rangeReturns a list of numbers between an inclusive lower bound and an exclusive upper bound.
Complexity: O(n)
Example
range(3, 8); //=> list(3, 4, 5, 6, 7)
repeatReturns a list of a given length that contains the specified value in all positions.
Complexity: O(n)
Example
repeat(1, 7); //=> list(1, 1, 1, 1, 1, 1, 1)
repeat("foo", 3); //=> list("foo", "foo", "foo")
timesReturns a list of given length that contains the value of the given function called with current index.
Complexity: O(n)
Example
const twoFirsOdds = times(i => i * 2 + 1, 2);
const dots = times(() => {
const x = Math.random() * width;
const y = Math.random() * height;
return { x, y };
}, 50);
concatConcatenates two lists.
Complexity: O(log(n))
Example
concat(list(0, 1, 2), list(3, 4)); //=> list(0, 1, 2, 3, 4)
flattenFlattens a list of lists into a list. Note that this function does not flatten recursively. It removes one level of nesting only.
Complexity: O(n * log(m)) where n is the length of the outer
list and m the length of the inner lists.
Example
const nested = list(list(0, 1, 2, 3), list(4), empty(), list(5, 6));
flatten(nested); //=> list(0, 1, 2, 3, 4, 5, 6)
prependPrepends an element to the front of a list and returns the new list.
Complexity: O(log(n)), practically constant
Example
const newList = prepend(0, list(1, 2, 3)); //=> list(0, 1, 2, 3)
appendAppends an element to the end of a list and returns the new list.
Complexity: O(log(n)), practically constant
Example
const newList = append(3, list(0, 1, 2)); //=> list(0, 1, 2, 3)
intersperseInserts a separator between each element in a list.
Example
intersperse("n", list("ba", "a", "a")); //=> list("ba", "n", "a", "n", "a")
mapApplies a function to each element in the given list and returns a new list of the values that the function return.
Complexity: O(n)
Example
map(n => n * n, list(0, 1, 2, 3, 4)); //=> list(0, 1, 4, 9, 16)
pluckExtracts the specified property from each object in the list.
Example
const l = list(
{ foo: 0, bar: "a" },
{ foo: 1, bar: "b" },
{ foo: 2, bar: "c" }
);
pluck("foo", l); //=> list(0, 1, 2)
updateReturns a list that has the entry specified by the index replaced with the given value.
If the index is out of bounds the given list is returned unchanged.
Complexity: O(log(n))
Example
update(2, "X", list("a", "b", "c", "d", "e")); //=> list("a", "b", "X", "d", "e")
adjustReturns a list that has the entry specified by the index replaced with the value returned by applying the function to the value.
If the index is out of bounds the given list is returned unchanged.
Complexity: O(log(n))
Example
adjust(2, inc, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 3, 3, 4, 5)
sliceReturns a slice of a list. Elements are removed from the beginning and end. Both the indices can be negative in which case they will count from the right end of the list.
Complexity: O(log(n))
Example
const l = list(0, 1, 2, 3, 4, 5);
slice(1, 4, l); //=> list(1, 2, 3)
slice(2, -2, l); //=> list(2, 3)
takeTakes the first n elements from a list and returns them in a new list.
Complexity: O(log(n))
Example
take(3, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 2)
takeWhileTakes the first elements in the list for which the predicate returns
true.
Complexity: O(k + log(n)) where k is the number of elements
satisfying the predicate.
Example
takeWhile(n => n < 4, list(0, 1, 2, 3, 4, 5, 6)); //=> list(0, 1, 2, 3)
takeWhile(_ => false, list(0, 1, 2, 3, 4, 5)); //=> list()
takeLastTakes the last n elements from a list and returns them in a new
list.
Complexity: O(log(n))
Example
takeLast(3, list(0, 1, 2, 3, 4, 5)); //=> list(3, 4, 5)
takeLastWhileTakes the last elements in the list for which the predicate returns
true.
Complexity: O(k + log(n)) where k is the number of elements
satisfying the predicate.
Example
takeLastWhile(n => n > 2, list(0, 1, 2, 3, 4, 5)); //=> list(3, 4, 5)
takeLastWhile(_ => false, list(0, 1, 2, 3, 4, 5)); //=> list()
splitAtSplits a list at the given index and return the two sides in a pair. The left side will contain all elements before but not including the element at the given index. The right side contains the element at the index and all elements after it.
Complexity: O(log(n))
Example
const l = list(0, 1, 2, 3, 4, 5, 6, 7, 8);
splitAt(4, l); //=> [list(0, 1, 2, 3), list(4, 5, 6, 7, 8)]
splitWhenSplits a list at the first element in the list for which the given
predicate returns true.
Complexity: O(n)
Example
const l = list(0, 1, 2, 3, 4, 5, 6, 7);
splitWhen((n) => n > 3, l); //=> [list(0, 1, 2, 3), list(4, 5, 6, 7)]
removeTakes an index, a number of elements to remove and a list. Returns a new list with the given amount of elements removed from the specified index.
Complexity: O(log(n))
Example
const l = list(0, 1, 2, 3, 4, 5, 6, 7, 8);
remove(4, 3, l); //=> list(0, 1, 2, 3, 7, 8)
remove(2, 5, l); //=> list(0, 1, 7, 8)
dropReturns a new list without the first n elements.
Complexity: O(log(n))
Example
drop(2, list(0, 1, 2, 3, 4, 5)); //=> list(2, 3, 4, 5)
dropWhileRemoves the first elements in the list for which the predicate returns
true.
Complexity: O(k + log(n)) where k is the number of elements
satisfying the predicate.
Example
dropWhile(n => n < 4, list(0, 1, 2, 3, 4, 5, 6)); //=> list(4, 5, 6)
dropLastReturns a new list without the last n elements.
Complexity: O(log(n))
Example
dropLast(2, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 2, 3)
dropRepeatsReturns a new list without repeated elements.
Complexity: O(n)
Example
dropRepeats(L.list(0, 0, 1, 1, 1, 2, 3, 3, 4, 4)); //=> list(0, 1, 2, 3, 4)
dropRepeatsWithReturns a new list without repeated elements by using the given function to determine when elements are equal.
Complexity: O(n)
Example
dropRepeatsWith(
(n, m) => Math.floor(n) === Math.floor(m),
list(0, 0.4, 1.2, 1.1, 1.8, 2.2, 3.8, 3.4, 4.7, 4.2)
); //=> list(0, 1, 2, 3, 4)
tailReturns a new list with the first element removed. If the list is empty the empty list is returne.
Complexity: O(1)
Example
tail(list(0, 1, 2, 3)); //=> list(1, 2, 3)
tail(empty()); //=> list()
popReturns a new list with the last element removed. If the list is empty the empty list is returned.
Aliases: init
Complexity: O(1)
Example
pop(list(0, 1, 2, 3)); //=> list(0, 1, 2)
filterReturns a new list that only contains the elements of the original
list for which the predicate returns true.
Complexity: O(n)
Example
filter(isEven, list(0, 1, 2, 3, 4, 5, 6)); //=> list(0, 2, 4, 6)
rejectReturns a new list that only contains the elements of the original
list for which the predicate returns false.
Complexity: O(n)
Example
reject(isEven, list(0, 1, 2, 3, 4, 5, 6)); //=> list(1, 3, 5)
reverseReverses a list.
Complexity: O(n)
Example
reverse(list(0, 1, 2, 3, 4, 5)); //=> list(5, 4, 3, 2, 1, 0)
apApplies a list of functions to a list of values.
Example
ap(list((n: number) => n + 2, n => 2 * n, n => n * n), list(1, 2, 3)); //=> list(3, 4, 5, 2, 4, 6, 1, 4, 9)
chainMaps a function over a list and concatenates all the resulting lists together.
Aliases: flatMap
Example
chain(n => list(n, 2 * n, n * n), list(1, 2, 3)); //=> list(1, 2, 1, 2, 4, 4, 3, 6, 9)
partitionSplits the list into two lists. One list that contains all the values
for which the predicate returns true and one containing the values for
which it returns false.
Complexity: O(n)
Example
partition(isEven, list(0, 1, 2, 3, 4, 5)); //=> list(list(0, 2, 4), list(1, 3, 5))
insertInserts the given element at the given index in the list.
Complexity: O(log(n))
Example
insert(2, "c", list("a", "b", "d", "e")); //=> list("a", "b", "c", "d", "e")
insertAllInserts the given list of elements at the given index in the list.
Complexity: O(log(n))
Example
insertAll(2, list("c", "d"), list("a", "b", "e", "f")); //=> list("a", "b", "c", "d", "e", "f")
zipWithThis is like mapping over two lists at the same time. The two lists are iterated over in parallel and each pair of elements is passed to the function. The returned values are assembled into a new list.
The shortest list determine the size of the result.
Complexity: O(log(n)) where n is the length of the smallest list.
Example
const names = list("Turing", "Curry");
const years = list(1912, 1900);
zipWith((name, year) => ({ name, year }), names, years);
//=> list({ name: "Turing", year: 1912 }, { name: "Curry", year: 1900 });
zipIterate over two lists in parallel and collect the pairs.
Complexity: O(log(n)) where n is the length of the smallest list.
Example
const names = list("a", "b", "c", "d", "e");
const years = list(0, 1, 2, 3, 4, 5, 6);
//=> list(["a", 0], ["b", 1], ["c", 2], ["d", 3], ["e", 4]);
sortSorts the given list. The list should contain values that can be compared using
the < operator or values that implement the Fantasy Land
Ord specification.
Performs a stable sort.
Complexity: O(n * log(n))
Example
sort(list(5, 3, 1, 8, 2)); //=> list(1, 2, 3, 5, 8)
sort(list("e", "a", "c", "b", "d"); //=> list("a", "b", "c", "d", "e")
sortBySort the given list by passing each value through the function and comparing
the resulting value. The function should either return values comparable using
< or values that implement the Fantasy Land
Ord specification.
Performs a stable sort.
Complexity: O(n * log(n))
Example
sortBy(
o => o.n,
list({ n: 4, m: "foo" }, { n: 3, m: "bar" }, { n: 1, m: "baz" })
);
//=> list({ n: 1, m: "baz" }, { n: 3, m: "bar" }, { n: 4, m: "foo" })
sortBy(s => s.length, list("foo", "bar", "ba", "aa", "list", "z"));
//=> list("z", "ba", "aa", "foo", "bar", "list")
sortWithSort the given list by comparing values using the given function. The function
receieves two values and should return -1 if the first value is stricty
larger than the second, 0 is they are equal and 1 if the first values is
strictly smaller than the second.
Note that the comparison function is equivalent to the one required by
Array.prototype.sort.
Performs a stable sort.
Complexity: O(n * log(n))
Example
sortWith((a, b) => {
if (a === b) {
return 0;
} else if (a < b) {
return -1;
} else {
return 1;
}
}, list(5, 3, 1, 8, 2)); //=> list(1, 2, 3, 5, 8)
groupReturns a list of lists where each sublist's elements are all equal.
Complexity: O(n)
Example
group(list(0, 0, 1, 2, 2, 2, 3, 3)); //=> list(list(0, 0), list(1), list(2, 2, 2), list(3, 3))
groupWithReturns a list of lists where each sublist's elements are pairwise equal based on the given comparison function.
Note that only adjacent elements are compared for equality. If all equal elements should be grouped together the list should be sorted before grouping.
Complexity: O(n)
Example
const floorEqual = (a, b) => Math.round(a) === Math.round(b);
groupWith(floorEqual, list(1.1, 1.3, 1.8, 2, 2.2, 3.3, 3.4));
//=> list(list(1.1, 1.3), list(1.8, 2, 2.2), list(3.3, 3.4))
const sameLength = (a, b) => a.length === b.length;
groupWith(sameLength, list("foo", "bar", "ab", "bc", "baz"));
//=> list(list("foo", "bar"), list("ab", "bc"), list("baz))
isListReturns true if the given argument is a list.
Complexity: O(1)
Example
isList([0, 1, 2]); //=> false
isList("string"); //=> false
isList({ foo: 0, bar: 1 }); //=> false
isList(list(0, 1, 2)); //=> true
isEmptyReturns true if the list is empty.
Complexity: O(1)
Example
isEmpty(empty()); //=> true
isEmpty(list()); //=> true
isEmpty(list(0, 1, 2)); //=> false
equalsReturns true if the two lists are equivalent.
Complexity: O(n)
Example
equals(list(0, 1, 2, 3), list(0, 1, 2, 3)); //=> true
equals(list("a", "b", "c"), list("a", "z", "c")); //=> false
equalsWithReturns true if the two lists are equivalent when comparing each
pair of elements with the given comparison function.
Complexity: O(n)
Example
equalsWith(
(n, m) => n.length === m.length,
list("foo", "hello", "one"),
list("bar", "world", "two")
); //=> true
toArrayConverts a list into an array.
Complexity: O(n)
Example
toArray(list(0, 1, 2, 3, 4)); //=> [0, 1, 2, 3, 4]
backwardsReturns an iterable that iterates backwards over the given list.
Complexity: O(1)
Example
const l = list(0, 1, 2, 3, 4)
for (const n of backwards(l)) {
if (l < 2) {
break;
}
console.log(l);
}
// => logs 4, 3, and then 2
nthGets the nth element of the list. If n is out of bounds
undefined is returned.
Complexity: O(log(n)), practically constant
Example
const l = list(0, 1, 2, 3, 4);
nth(2, l); //=> 2
lengthReturns the length of a list. I.e. the number of elements that it contains.
Complexity: O(1)
Example
length(list(0, 1, 2, 3)); //=> 4
firstReturns the first element of the list. If the list is empty the
function returns undefined.
Aliases: head
Complexity: O(1)
Example
first(list(0, 1, 2, 3)); //=> 0
first(list()); //=> undefined
lastReturns the last element of the list. If the list is empty the
function returns undefined.
Complexity: O(1)
Example
last(list(0, 1, 2, 3)); //=> 3
last(list()); //=> undefined
foldlFolds a function over a list. Left-associative.
Aliases: reduce
Complexity: O(n)
Example
foldl((n, m) => n - m, 1, list(2, 3, 4, 5));
1 - 2 - 3 - 4 - 5; //=> -13
foldrFolds a function over a list. Right-associative.
Aliases: reduceRight
Complexity: O(n)
Example
foldr((n, m) => n - m, 5, list(1, 2, 3, 4));
1 - (2 - (3 - (4 - 5))); //=> 3
foldlWhileSimilar to foldl. But, for each element it calls the predicate function
before the folding function and stops folding if it returns false.
Aliases: reduceWhile
Complexity: O(n)
Example
const isOdd = (_acc:, x) => x % 2 === 1;
const xs = L.list(1, 3, 5, 60, 777, 800);
foldlWhile(isOdd, (n, m) => n + m, 0, xs) //=> 9
const ys = L.list(2, 4, 6);
foldlWhile(isOdd, (n, m) => n + m, 111, ys) //=> 111
scanFolds a function over a list from left to right while collecting all the intermediate steps in a resulting list.
Complexity: O(n)
Example
const l = list(1, 3, 5, 4, 2);
L.scan((n, m) => n + m, 0, l); //=> list(0, 1, 4, 9, 13, 15));
L.scan((s, m) => s + m.toString(), "", l); //=> list("", "1", "13", "135", "1354", "13542")
traverseMap each element of list to an applicative, evaluate these applicatives from left to right, and collect the results.
This works with Fantasy Land applicatives.
Complexity: O(n)
Example
const safeDiv = n => d => d === 0 ? nothing : just(n / d)
L.traverse(Maybe, safeDiv(10), list(2, 4, 5)); //=> just(list(5, 2.5, 2))
L.traverse(Maybe, safeDiv(10), list(2, 0, 5)); //=> nothing
sequenceEvaluate each applicative in the list from left to right, and collect the results.
Complexity: O(n)
Example
L.sequence(Maybe, list(just(1), just(2), just(3))); //=> just(list(1, 2, 3))
L.sequence(Maybe, list(just(1), just(2), nothing())); //=> nothing
forEachInvokes a given callback for each element in the list from left to
right. Returns undefined.
This function is very similar to map. It should be used instead of
map when the mapping function has side-effects. Whereas map
constructs a new list forEach merely returns undefined. This makes
forEach faster when the new list is unneeded.
Complexity: O(n)
Example
const l = list(0, 1, 2);
forEach(element => console.log(element));
//=> 0
//=> 1
//=> 2
everyReturns true if and only if the predicate function returns true
for all elements in the given list.
Aliases: all
Complexity: O(n)
Example
const isEven = n => n % 2 === 0;
every(isEven, empty()); //=> true
every(isEven, list(2, 4, 6, 8)); //=> true
every(isEven, list(2, 3, 4, 6, 7, 8)); //=> false
every(isEven, list(1, 3, 5, 7)); //=> false
someReturns true if and only if there exists an element in the list for
which the predicate returns true.
Aliases: any
Complexity: O(n)
Example
const isEven = n => n % 2 === 0;
some(isEven, empty()); //=> false
some(isEven, list(2, 4, 6, 8)); //=> true
some(isEven, list(2, 3, 4, 6, 7, 8)); //=> true
some(isEven, list(1, 3, 5, 7)); //=> false
indexOfReturns the index of the first element in the list that is equal to
the given element. If no such element is found -1 is returned.
Complexity: O(n)
Example
const l = list(12, 4, 2, 89, 6, 18, 7);
indexOf(12, l); //=> 0
indexOf(89, l); //=> 3
indexOf(10, l); //=> -1
lastIndexOfReturns the index of the last element in the list that is equal to
the given element. If no such element is found -1 is returned.
Complexity: O(n)
Example
const l = L.list(12, 4, 2, 18, 89, 2, 18, 7);
L.lastIndexOf(18, l); //=> 6
L.lastIndexOf(2, l); //=> 5
L.lastIndexOf(12, l); //=> 0
findReturns the first element for which the predicate returns true. If
no such element is found the function returns undefined.
Complexity: O(n)
Example
find(isEven, list(1, 3, 5, 6, 7, 9, 10)); //=> 6
find(isEven, list(1, 3, 5, 7, 9)); //=> undefined
findLastReturns the last element for which the predicate returns true. If
no such element is found the function returns undefined.
Complexity: O(n)
Example
findLast(isEven, list(1, 3, 5, 6, 7, 8, 9)); //=> 8
findLast(isEven, list(1, 3, 5, 7, 9)); //=> undefined
findIndexReturns the index of the first element for which the predicate returns
true. If no such element is found the function returns -1.
Complexity: O(n)
Example
findIndex(isEven, list(1, 3, 5, 6, 7, 9, 10)); //=> 3
findIndex(isEven, list(1, 3, 5, 7, 9)); //=> -1
noneReturns true if and only if the predicate function returns false
for all elements in the given list.
Complexity: O(n)
Example
const isEven = n => n % 2 === 0;
none(isEven, empty()); //=> true
none(isEven, list(2, 4, 6, 8)); //=> false
none(isEven, list(2, 3, 4, 6, 7, 8)); //=> false
none(isEven, list(1, 3, 5, 7)); //=> true
includesReturns true if the list contains the specified element. Otherwise
it returns false.
Aliases: contains
Complexity: O(n)
Example
includes(3, list(0, 1, 2, 3, 4, 5)); //=> true
includes(3, list(0, 1, 2, 4, 5)); //=> false
joinConcats the strings in a list separated by a specified separator.
Complexity: O(n)
Example
join(", ", list("one", "two", "three")); //=> "one, two, three"
The benchmarks are located in the bench directory.
Overall
Maintained
Code review
npm
Latest release
Releases this year
1,568 followers · starred May 2018
1,522 followers · starred Oct 2019
5,054 followers · starred Jun 2018
515 followers · starred May 2018