UNPKG

18.4 kBMarkdownView Raw
1<h3 align="center">
2 <img align="center" src="assets/listopard.png" alt="List logo" width=400 />
3</h3>
4
5<p align="center">
6A fast immutable list with a functional API.
7</p>
8
9<p align="center">
10 <a href="https://gitter.im/funkia/General"><img src="https://img.shields.io/gitter/room/funkia/General.svg" alt="Gitter" height="20"></a>
11 <a href="https://travis-ci.org/funkia/list"><img src="https://travis-ci.org/funkia/list.svg?branch=master" alt="Build Status" height="20"></a>
12 <a href="https://codecov.io/gh/funkia/list"><img src="https://codecov.io/gh/funkia/list/branch/master/graph/badge.svg" alt="codecov" height="20"></a>
13 <a href="https://badge.fury.io/js/list"><img src="https://badge.fury.io/js/list.svg" alt="npm version" height="20"></a>
14 <a href="https://david-dm.org/funkia/list"><img src="https://david-dm.org/funkia/list/status.svg" alt="npm version" height="20"></a>
15 <a href="https://david-dm.org/funkia/list?type=dev"><img src="https://david-dm.org/funkia/list/dev-status.svg" alt="npm version" height="20"></a>
16</p>
17
18# List
19
20List is a purely functional alternative to arrays. It is an
21implementation of a fast persistent sequence data structure. Compared
22to JavaScript's `Array` List has two major benefits.
23
24* **Safety**. List is immutable. This makes it safer and better suited
25 for functional programming. It doesn't tempt you with an imperative
26 API and accidental mutations won't be a source bugs.
27* **Performance**. Since List doesn't allow mutations it can be
28 heavily optimized for pure operations. This makes List much faster
29 for functional programming than arrays.
30
31## Features
32
33* **Familiar functional API**. List follows the naming conventions
34 common in functional programming and has arguments ordered for
35 currying/partial application.
36* **Extensive API**. List has all the functions you know from `Array`
37 and a lot of other functions that I'll save the day once you need
38 them.
39* **Extremely fast**. List is a carefully optimized implementation of
40 the highly efficient data-structure _relaxed radix balanced trees_.
41 Performance has been a central focus from the beginning and we have
42 an [extensive benchmark suite](/test/bench) to ensure optimal
43 performance.
44* **Does one thing well**. Instead of offering a wealth of data
45 structures List has a tight focus on being the best immutable list
46 possible. It doesn't do everything but is designed to work well with
47 the libraries you're already using.
48* **Seamless Ramda integration**. If you know Ramda you already know
49 how to use List. List includes a [Ramda
50 specific](#seamless-ramda-integration) module where functions are
51 curried using Ramda's `R.curry` and where equality comparisons are
52 done using `R.equals`.
53* **Type safe**. List is written in TypeScript and comes with accurate
54 types that cover the entire library.
55* **Fantasy Land support**. List
56 [implements](#fantasy-land-static-land) both the Fantasy Land and
57 the Static Land specification.
58* **Fully compatible with tree-shaking**. List ships with tree-shaking
59 compatible ECMAScript modules. `import * as L from "list"` in itself
60 adds zero bytes to your bundle when using Webpack. Using a function
61 adds only that function and the very small (<1KB) core of the
62 library. You only pay in size for the functions that you actually
63 use.
64
65## Install
66
67```
68npm install list
69```
70
71## Seamless Ramda integration
72
73List is designed to work seamlessly together with Ramda. Ramda offers
74a large number of useful functions for working with arrays. List
75implements the same methods on its immutable data structure. This
76means that Ramda users can keep using the API they're familiar with.
77
78Additionally, List offers an entry point where all functions are
79curried using Ramda's `R.curry` and where all equality comparisons are
80done using `R.equals`.
81
82```js
83import * as L from "list/ramda";
84const indexOfFoo1 = indexOf({ foo: 1 });
85indexOfFoo1({ foo: 0 }, { foo: 1 }, { foo: 2 }); //=> 1
86```
87
88In the example above `indexOf` is curried and it uses `R.equals` to
89find an element equivalent to `{foo: 1}`.
90
91Since List implements Ramda's array API it is very easy to convert
92code from using arrays to using immutable lists. As an example,
93consider the code below.
94
95```js
96import * as R from "ramda";
97
98R.pipe(R.filter(n => n % 2 === 0), R.map(R.multiply(3)), R.reduce(R.add, 0))(
99 array
100);
101```
102
103It can be converted to code using List as follows.
104
105```js
106import * as R from "ramda";
107import * as L from "list";
108
109R.pipe(L.filter(n => n % 2 === 0), L.map(R.multiply(3)), L.reduce(R.add, 0))(
110 list
111);
112```
113
114For each function operating on arrays, the `R` is simply changed to an
115`L`. This works because List exports functions that have the same name
116and behavior as Ramdas functions.
117
118### Implemented Ramda functions
119
120The goal is to implement the entirety of Ramda's array functions for
121List. The list below keeps track of how many of Ramda functions that
122are missing and of how many that are already implemented. Currently 41
123out of 114 functions have been implemented.
124
125Implemented: `adjust`, `all`, `any`, `append`, `concat`, `contains`,
126`drop`, `dropLast`, `dropWhile`, `filter`, `find`, `findIndex`,
127`head`, `flatten`, `indexOf`, `init`, `insert`, `insertAll`, `last`,
128`length`, `join`, `map`, `none`, `nth`, `pair`, `partition`, `pluck`,
129`prepend`, `range`, `reduce`, `reduceRight`, `reject`, `remove`,
130`repeat`, `slice`, `splitAt`, `take`, `takeWhile`, `tail`, `takeLast`,
131`update`.
132
133Not implemented: `aperture`, `chain`, `dropLastWhile`, `dropRepeats`,
134`dropRepeatsWith`, `endsWith`, `findLast`, `findLastIndex`, `groupBy`,
135`groupWith`, `indexBy`, `intersperse`, `lastIndexOf`, `mapAccum`,
136`mapAccumRight`, `mergeAll`, `reduceBy`, `reduceWhile`, `reverse`,
137`scan`, `sequence`, `sort`, `splitEvery`, `splitWhen`, `startsWith`,
138`takeLastWhile`, `times`, `transpose`, `traverse`, `unfold`, `uniq`,
139`uniqBy`, `uniqWith`, `unnest` `without`, `xprod`, `zip`, `zipObj`,
140`zipWith`.
141
142## Fantasy Land & Static Land
143
144<img align="right" width="82" height="82" alt="Fantasy Land" src="https://raw.github.com/fantasyland/fantasy-land/master/logo.png">
145<img align="right" width="131" height="82" src="https://raw.githubusercontent.com/rpominov/static-land/master/logo/logo.png" />
146
147List currently implements the following Fantasy Land and Static Land
148specifications: Setoid, semigroup, monoid, foldable, functor.
149
150The following specifications have not been implemented yet: Apply,
151applicative, traversable, chain, monad.
152
153Since methods hinder tree-shaking the Fantasy Land methods are not
154included by default. In order to get them you must import it likes this:
155
156```js
157import "list/fantasy-land";
158```
159
160## API documentation
161
162The API is organized into three parts.
163
1641. [Creating lists](#creating-lists) — Functions that _create_ lists.
1652. [Updating lists](#updating-lists) — Functions that _transform_ lits.
166 That is, functions that take one or more lists as arguments and
167 returns a new list.
1683. [Folds](#folds) — Functions that _extracts_ values based on lists.
169 They take one or more lists as arguments and returns something that
170 is not a list.
171
172### Creating lists
173
174### `list`
175
176Creates a list based on the arguments given.
177
178**Complexity**: `O(n)`
179
180**Example**
181
182```js
183const l = list(1, 2, 3, 4); // creates a list of four elements
184const l2 = list("foo"); // creates a singleton
185```
186
187### `empty`
188
189Returns an empty list.
190
191**Complexity**: `O(1)`
192
193**Example**
194
195```js
196const emptyList = empty(); //=> list()
197```
198
199### `pair`
200
201Takes two arguments and returns a list that contains them.
202
203**Complexity**: `O(1)`
204
205**Example**
206
207```js
208pair("foo", "bar"); //=> list("foo", "bar")
209```
210
211### `fromArray`
212
213Converts an array into a list.
214
215**Complexity**: `O(n)`
216
217**Example**
218
219```js
220fromArray([0, 1, 2, 3, 4]); //=> list(0, 1, 2, 3, 4)
221```
222
223### `range`
224
225Returns a list of numbers between an inclusive lower bound and an
226exclusive upper bound.
227
228**Complexity**: `O(n)`
229
230**Example**
231
232```js
233range(3, 8); //=> list(3, 4, 5, 6, 7)
234```
235
236### `repeat`
237
238Returns a list of a given length that contains the specified value in
239all positions.
240
241**Complexity**: `O(n)`
242
243**Example**
244
245```js
246repeat(1, 7); //=> list(1, 1, 1, 1, 1, 1, 1)
247repeat("foo", 3); //=> list("foo", "foo", "foo")
248```
249
250### Updating lists
251
252### `concat`
253
254Concatenates two lists.
255
256**Complexity**: `O(logn)`
257
258**Example**
259
260```js
261concat(list(0, 1, 2), list(3, 4)); //=> list(0, 1, 2, 3, 4)
262```
263
264### `flatten`
265
266Flattens a list of lists into a list. Note that this function does
267_not_ flatten recursively. It removes one level of nesting only.
268
269**Complexity**: `O(n * log(m))` where `n` is the length of the outer
270list and `m` the length of the inner lists.
271
272**Example**
273
274```js
275const nested = list(list(0, 1, 2, 3), list(4), empty(), list(5, 6));
276flatten(nested); //=> list(0, 1, 2, 3, 4, 5, 6)
277```
278
279### `prepend`
280
281Prepends an element to the front of a list and returns the new list.
282
283**Complexity**: `O(logn)`, practically constant
284
285**Example**
286
287```js
288const newList = prepend(0, list(1, 2, 3)); //=> list(0, 1, 2, 3)
289```
290
291### `append`
292
293Appends an element to the end of a list and returns the new list.
294
295**Complexity**: `O(logn)`, practically constant
296
297**Example**
298
299```js
300const newList = append(3, list(0, 1, 2)); //=> list(0, 1, 2, 3)
301```
302
303### `map`
304
305Applies a function to each element in the given list and returns a new
306list of the values that the function return.
307
308**Complexity**: `O(n)`
309
310**Example**
311
312```js
313map(n => n * n, list(0, 1, 2, 3, 4)); //=> list(0, 1, 4, 9, 12)
314```
315
316### `pluck`
317
318Extracts the specified property from each object in the list.
319
320**Example**
321
322```js
323const l = list(
324 { foo: 0, bar: "a" },
325 { foo: 1, bar: "b" },
326 { foo: 2, bar: "c" }
327);
328pluck("foo", l); //=> list(0, 1, 2)
329```
330
331### `update`
332
333Returns a list that has the entry specified by the index replaced with
334the given value.
335
336**Complexity**: `O(logn)`
337
338**Example**
339
340```js
341update(2, "X", list("a", "b", "c", "d", "e")); //=> list("a", "b", "X", "d", "e")
342```
343
344### `adjust`
345
346Returns a list that has the entry specified by the index replaced with
347the value returned by applying the function to the value.
348
349**Complexity**: `O(logn)`
350
351**Example**
352
353```js
354adjust(2, inc, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 3, 3, 4, 5)
355```
356
357### `slice`
358
359Returns a slice of a list. Elements are removed from the beginning and
360end. Both the indices can be negative in which case they will count
361from the right end of the list.
362
363**Complexity**: `O(log(n))`
364
365**Example**
366
367```js
368const l = list(0, 1, 2, 3, 4, 5);
369slice(1, 4, l); //=> list(1, 2, 3)
370slice(2, -2, l); //=> list(2, 3)
371```
372
373### `take`
374
375Takes the first `n` elements from a list and returns them in a new list.
376
377**Complexity**: `O(log(n))`
378
379**Example**
380
381```js
382take(3, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 2)
383```
384
385### `takeWhile`
386
387Takes the first elements in the list for which the predicate returns
388`true`.
389
390**Complexity**: `O(k + log(n))` where `k` is the number of elements
391satisfying the predicate.
392
393**Example**
394
395```js
396takeWhile(n => n < 4, list(0, 1, 2, 3, 4, 5, 6)); //=> list(0, 1, 2, 3)
397```
398
399### `takeLast`
400
401Takes the last `n` elements from a list and returns them in a new
402list.
403
404**Complexity**: `O(log(n))`
405
406**Example**
407
408```js
409takeLast(3, list(0, 1, 2, 3, 4, 5)); //=> list(3, 4, 5)
410```
411
412### `splitAt`
413
414Splits a list at the given index and return the two sides in a pair.
415The left side will contain all elements before but not including the
416element at the given index. The right side contains the element at the
417index and all elements after it.
418
419**Complexity**: `O(log(n))`
420
421**Example**
422
423```js
424const l = list(0, 1, 2, 3, 4, 5, 6, 7, 8);
425splitAt(4, l); //=> [list(0, 1, 2, 3), list(4, 5, 6, 7, 8)]
426```
427
428### `remove`
429
430Takes an index, a number of elements to remove and a list. Returns a
431new list with the given amount of elements removed from the specified
432index.
433
434**Complexity**: `O(log(n))`
435
436**Example**
437
438```js
439const l = list(0, 1, 2, 3, 4, 5, 6, 7, 8);
440remove(4, 3, l); //=> list(0, 1, 2, 3, 7, 8)
441remove(2, 5, l); //=> list(0, 1, 7, 8)
442```
443
444### `drop`
445
446Returns a new list without the first `n` elements.
447
448**Complexity**: `O(log(n))`
449
450**Example**
451
452```js
453drop(2, list(0, 1, 2, 3, 4, 5)); //=> list(2, 3, 4, 5)
454```
455
456### `dropWhile`
457
458Removes the first elements in the list for which the predicate returns
459`true`.
460
461**Complexity**: `O(k + log(n))` where `k` is the number of elements
462satisfying the predicate.
463
464**Example**
465
466```js
467dropWhile(n => n < 4, list(0, 1, 2, 3, 4, 5, 6)); //=> list(4, 5, 6)
468```
469
470### `dropLast`
471
472Returns a new list without the first `n` elements.
473
474**Complexity**: `O(log(n))`
475
476**Example**
477
478```js
479dropLast(2, list(0, 1, 2, 3, 4, 5)); //=> list(0, 1, 2, 3)
480```
481
482### `tail`
483
484Returns a new list with the first element removed.
485
486**Complexity**: `O(1)`
487
488**Example**
489
490```js
491tail(list(0, 1, 2, 3)); //=> list(1, 2, 3)
492```
493
494### `pop`
495
496Returns a new list with the last element removed.
497
498**Aliases**: `init`
499
500**Complexity**: `O(1)`
501
502**Example**
503
504```js
505pop(list(0, 1, 2, 3)); //=> list(0, 1, 2)
506```
507
508### `filter`
509
510Returns a new list that only contains the elements of the original
511list for which the predicate returns `true`.
512
513**Complexity**: `O(n)`
514
515**Example**
516
517```js
518filter(isEven, list(0, 1, 2, 3, 4, 5, 6)); //=> list(0, 2, 4, 6)
519```
520
521### `reject`
522
523Returns a new list that only contains the elements of the original
524list for which the predicate returns `false`.
525
526**Complexity**: `O(n)`
527
528**Example**
529
530```js
531reject(isEven, list(0, 1, 2, 3, 4, 5, 6)); //=> list(1, 3, 5)
532```
533
534### `partition`
535
536Splits the list into two lists. One list that contains all the values
537for which the predicate returns `true` and one containing the values for
538which it returns `false`.
539
540**Complexity**: `O(n)`
541
542**Example**
543
544```js
545partition(isEven, list(0, 1, 2, 3, 4, 5)); //=> list(list(0, 2, 4), list(1, 3, 5))
546```
547
548### `insert`
549
550Inserts the given element at the given index in the list.
551
552**Complexity**: `O(log(n))`
553
554**Example**
555
556```js
557insert(2, "c", list("a", "b", "d", "e")); //=> list("a", "b", "c", "d", "e")
558```
559
560### `insertAll`
561
562Inserts the given list of elements at the given index in the list.
563
564**Complexity**: `O(log(n))`
565
566**Example**
567
568```js
569insertAll(2, list("c", "d"), list("a", "b", "e", "f")); //=> list("a", "b", "c", "d", "e", "f")
570```
571
572### Folds
573
574### `equals`
575
576Returns true if the two lists are equivalent.
577
578**Complexity**: `O(n)`
579
580**Example**
581
582```js
583equals(list(0, 1, 2, 3), list(0, 1, 2, 3)); //=> true
584equals(list("a", "b", "c"), list("a", "z", "c")); //=> false
585```
586
587### `toArray`
588
589Converts a list into an array.
590
591**Complexity**: `O(n)`
592
593**Example**
594
595```js
596toArray(list(0, 1, 2, 3, 4)); //=> [0, 1, 2, 3, 4]
597```
598
599### `nth`
600
601Gets the `n`th element of the list.
602
603**Complexity**: `O(logn)`, practically constant
604
605**Example**
606
607```js
608const l = list(0, 1, 2, 3, 4);
609nth(2, l); //=> 2
610```
611
612### `length`
613
614Returns the length of a list. I.e. the number of elements that it
615contains.
616
617**Complexity**: `O(1)`
618
619**Example**
620
621```js
622length(list(0, 1, 2, 3)); //=> 4
623```
624
625### `first`
626
627Returns the first element of the list. If the list is empty the
628function returns `undefined`.
629
630**Aliases**: `head`
631
632**Complexity**: `O(1)`
633
634**Example**
635
636```js
637first(list(0, 1, 2, 3)); //=> 0
638first(list()); //=> undefined
639```
640
641### `last`
642
643Returns the last element of the list. If the list is empty the
644function returns `undefined`.
645
646**Complexity**: `O(1)`
647
648**Example**
649
650```js
651last(list(0, 1, 2, 3)); //=> 3
652last(list()); //=> undefined
653```
654
655### `foldl`
656
657Folds a function over a list. Left-associative.
658
659**Aliases**: `reduce`
660
661**Complexity**: `O(n)`
662
663**Example**
664
665```js
666foldl((n, m) => n - m, 1, list(2, 3, 4, 5));
6671 - 2 - 3 - 4 - 5; //=> -13
668```
669
670### `foldr`
671
672Folds a function over a list. Right-associative.
673
674**Aliases**: `reduceRight`
675**Aliases**: `reduceRight`
676
677**Complexity**: `O(n)`
678
679**Example**
680
681```js
682foldr((n, m) => n - m, 5, list(1, 2, 3, 4));
6831 - (2 - (3 - (4 - 5))); //=> 3
684```
685
686### `every`
687
688Returns `true` if and only if the predicate function returns `true`
689for all elements in the given list.
690
691**Aliases**: `all`
692
693**Complexity**: `O(n)`
694
695**Example**
696
697```js
698const isEven = n => n % 2 === 0;
699every(isEven, empty()); //=> true
700every(isEven, list(2, 4, 6, 8)); //=> true
701every(isEven, list(2, 3, 4, 6, 7, 8)); //=> false
702every(isEven, list(1, 3, 5, 7)); //=> false
703```
704
705### `some`
706
707Returns `true` if and only if there exists an element in the list for
708which the predicate returns `true`.
709
710**Aliases**: `any`
711
712**Complexity**: `O(n)`
713
714**Example**
715
716```js
717const isEven = n => n % 2 === 0;
718some(isEven, empty()); //=> false
719some(isEven, list(2, 4, 6, 8)); //=> true
720some(isEven, list(2, 3, 4, 6, 7, 8)); //=> true
721some(isEven, list(1, 3, 5, 7)); //=> false
722```
723
724### `indexOf`
725
726Returns the index of the first element in the list that is equal to
727the given element. If no such element is found the function returns
728`-1`.
729
730**Complexity**: `O(n)`
731
732**Example**
733
734```js
735const l = list(12, 4, 2, 89, 6, 18, 7);
736indexOf(12, l); //=> 0
737indexOf(89, l); //=> 3
738indexOf(10, l); //=> -1
739```
740
741### `find`
742
743Returns the first element for which the predicate returns `true`. If
744no such element is found the function returns `undefined`.
745
746**Complexity**: `O(n)`
747
748**Example**
749
750```js
751find(isEven, list(1, 3, 5, 6, 7, 9, 10)); //=> 6
752find(isEven, list(1, 3, 5, 7, 9)); //=> undefined
753```
754
755### `findIndex`
756
757Returns the index of the first element for which the predicate returns
758`true`. If no such element is found the function returns `-1`.
759
760**Complexity**: `O(n)`
761
762**Example**
763
764```js
765findIndex(isEven, list(1, 3, 5, 6, 7, 9, 10)); //=> 3
766findIndex(isEven, list(1, 3, 5, 7, 9)); //=> -1
767```
768
769### `none`
770
771Returns `true` if and only if the predicate function returns `false`
772for all elements in the given list.
773
774**Complexity**: `O(n)`
775
776**Example**
777
778```js
779const isEven = n => n % 2 === 0;
780none(isEven, empty()); //=> true
781none(isEven, list(2, 4, 6, 8)); //=> false
782none(isEven, list(2, 3, 4, 6, 7, 8)); //=> false
783none(isEven, list(1, 3, 5, 7)); //=> true
784```
785
786### `includes`
787
788Returns `true` if the list contains the specified element. Otherwise
789it returns `false`.
790
791**Aliases**: `contains`
792
793**Complexity**: `O(n)`
794
795**Example**
796
797```js
798includes(3, list(0, 1, 2, 3, 4, 5)); //=> true
799includes(3, list(0, 1, 2, 4, 5)); //=> false
800```
801
802### `join`
803
804Concats the strings in a list separated by a specified separator.
805
806**Complexity**: `O(n)`
807
808**Example**
809
810```js
811join(", ", list("one", "two", "three")); //=> "one, two, three"
812```
813
814## Benchmarks
815
816The benchmarks are located in the [`bench` directory](/test/bench).