Skip to content
/ listed Public

A functional, performant, list processing library designed to take advantage of Array subclassing.

License

Notifications You must be signed in to change notification settings

dvlsg/listed

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

listed

A functional, performant, list processing library designed to take advantage of Array subclassing.

// main example to come

Installation

npm install listed

Usage

A List is an extended array, which contains additional functional extensions to assist with processing data.

const { List } = require('listed');
let list = List.of(1, 2, 3, 4, 5);

In order to use this library, Array subclass must be available to you. Please see the ES compatibility table for more information.

Default Task

  • Install node.js
  • Clone the listed project
  • Run npm install
  • Run gulp
    • Executes tests
    • Lints source code
    • Starts a watch task on source and tests

Benchmarks

To run benchmarks of performance comparisons with other popular data manipulation libraries, run gulp perf, or run any of the ./perf/listed-*.perf.js files through node.

API

Links

List

.from()

List.from :: (arrayLike<T> | Array<T> | Iterable<T>) -> List<T>

Converts an existing Array, array-like, or iterable Object to a new List.

const list = List.from([ 1, 2, 3 ]);
//=> List [ 1, 2, 3 ]

.of()

List.of :: (...T) -> List<T>

Creates a new List containing all of the provided arguments.

const list = List.of(1, 2, 3);
//=> List [ 1, 2, 3 ]

Note that this will work even with a single Number argument, whereas new List(number) will create a List with the length of that argument.

const list = List.of(1);
//=> List [ 1 ]

#any()

List#any :: List<T> ~> (T -> Boolean) -> Boolean
List#any :: List<T> ~> () -> Boolean

Aliases: some

Returns a boolean indicating whether any of the elements in the List pass a given predicate.

const list = List.of(1, 2, 3, 4, 5);
const any = list.any(x => x > 4);
//=> true

If no predicate is provided, then the identity function will be used for a predicate.

const list = List.of(null, undefined, 0, '');
const any = list.any();
//=> false

#average()

List#average :: List<Number> ~> () -> Number
List#average :: List<T> ~> ((T) -> Number) -> Number

Returns the average of all numbers in the List.

const list = List.of(1, 2, 3, 4, 5);
const average = list.average();
//=> 3

Accepts an optional selector to determine what elements to average.

const list = List.of(
  { id: 1, count: 3 },
  { id: 2, count: 2 },
  { id: 3, count: 4 },
  { id: 4, count: 1 }
);
const average = list.average(elem => elem.count);
//=> 2.5

The average of an empty List will be NaN. Additional reading here.

const list = List.of();
const average = list.average();
//=> NaN

#every()

List#every :: List<T> ~> (T -> Boolean) -> Boolean

Aliases: all

Returns a Boolean indicating whether or not every value passes a given predicate. The index of the element, and a reference to the original List will also be provided to the predicate.

const list = List.of(1, 2, 3);
const passed = list.every(x => typeof x === 'number');
//=> true

If no predicate is provided, then the identity function will be used as a default.

const list = List.of(0, 1, 2, 3);
const passed = list.every();
//=> false

Calling every() on an empty List will return true.

const list = List.of();
const passed = list.every();
//=> true

#filter()

List#filter :: List<T> ~> ((T, Number, List<T>) -> Boolean) -> List<T>

Returns a new List only containing elements which pass the given predicate.

const list = List.of(1, 2, 3);
const filtered = list.filter(elem => elem > 1);
//=> List [ 2, 3 ]

The index of the element will be provided to the given predicate.

const list = List.of(1, 2, 3);
const filtered = list.filter((elem, index) => index > 1);
//=> List [ 3 ]

A reference to the original List will also be provided to the given predicate.

const list = List.of(1, 2, 3);
const filtered = list.filter((elem, index, listRef) => {
  console.log(list === listRef); //=> true
});

If no predicate is provided, then the identity function (x => x) will be used as a default.

const list = List.of(0, 1, 2, null, 3);
const filtered = list.filter();
//=> List [ 1, 2, 3 ]

#first()

List#first :: List<T> ~> () -> T

Returns the first element from the List.

const list = List.of(1, 2, 3);
const first = list.first();
//=> 1

If the List is empty, then undefined will be returned.

const List = List.of();
const first = list.first();
//=> undefined

#flatMap()

List#flatMap :: List<T> ~> ((T, Number, List<T>) -> U | U[]) -> List<U>

Maps the List using a given mapper and flattens those results by one level.

let list = List.of(1, 2, 3);
let mapped = list.flatMap(x => [ x, x ]);
//=> [ 1, 1, 2, 2, 3, 3 ]

#flatten()

List#flatten :: List<T | T[]> ~> () -> List<T>
List#flatten :: List<T | T[]> ~> (Number) -> List<T>

Flattens the List by one or a given number of levels.

const list = List.of(1, 2, List.of(3, 4, List.of(5)));
const flattened = list.flatten();
//=> List [ 1, 2, 3, 4, List [ 5 ] ]
const flattened2 = list.flatten(2);
//=> List [ 1, 2, 3, 4, 5 ]

Flatten will also work with array-likes contained within the parent List.

const list = List.of(1, 2, [ 3, 4 ]);
const flattened = list.flatten();
//=> List [ 1, 2, 3, 4 ]

#groupBy()

List#groupBy :: List<T> ~> (T -> String) -> List<GroupedList<String>>
List#groupBy :: List<T> ~> (String) -> List<GroupedList<String>>

Groups the List by a given selector.

const list = List.of(
  { id: 1, type: 'a', val: 5 },
  { id: 2, type: 'a', val: 6 },
  { id: 3, type: 'b', val: 7 },
  { id: 4, type: 'b', val: 8 }
);
const grouped = list.groupBy(x => x.type);
/*=> List [
  GroupedList [
    { id: 1, type: 'a', val: 5 },
    { id: 2, type: 'a', val: 6 }
  ],
  GroupedList [
    { id: 3, type: 'b', val: 7 },
    { id: 4, type: 'b', val: 8 }
  ]
] */

If the selector is provided as a string, then the equivalent of x => x[selector] will be used to determine by which value to group.

Note that the internal GroupedList will have a key property defined which will contain the value of the result of the given selector.

const list = List.of(
  { id: 1, type: 'a', val: 5 },
  { id: 2, type: 'a', val: 6 },
  { id: 3, type: 'b', val: 7 },
  { id: 4, type: 'b', val: 8 }
);
const result = list
  .groupBy(x => x.type)
  .map(group => ({
    key: group.key,
    average: group.average(x => x.val)
    sum: group.sum(x => x.val)
  }));
/* => List [
  { key: 'a', average: 5.5, sum: 11 },
  { key: 'b', average: 7.5, sum: 15 }
] */

#last()

List#last :: List<T> ~> () -> T

Returns the last element from the List.

const list = List.of(1, 2, 3);
const last = list.last();
//=> 3

If the List is empty, then undefined will be returned.

const List = List.of();
const last = list.last();
//=> undefined

#map()

List#map :: List<T> ~> ((T, Number, List<T>) -> U) -> List<U>

Returns a new List containing elements which have been mapped by the provided transformer.

const list = List.of(1, 2, 3);
const mapped = List.map(elem => elem + 1);
//=> List [ 2, 3, 4 ]

The index of the element will also be provided to the given transformer.

const list = List.of(1, 2, 3);
const mapped = List.map((elem, index) => elem + index);
//=> List [ 1, 3, 5 ]

A reference to the original List will also be provided to the given transformer.

const list = List.of(1, 2, 3);
const mapped = List.map((elem, index, listRef) => {
  console.log(list === listRef); //=> true
});

If no transformer is provided, then the identity function (x => x) will be used as a default.

const list = List.of(1, 2, 3);
const mapped = List.map();
//=> List [ 1, 2, 3 ]

#mapAsync()

List#mapAsync :: List<T> ~> (T -> Promise<T> | T) -> Promise<List<T>>

Asynchronously maps a List into a new List by using a provided asynchronous mapper. All mapping will be executed in parallel.

const list = List.of('/users.json', '/tasks.json');
const mapped = await list.mapAsync(async elem => {
  const response = await fetch(elem);
  const json = await response.json();
  return json;
});
//=> List [ <users_json_data>, <tasks_json_data> ]

Note that even though mapAsync() returns a promise, you can still chain multiple List methods, including additional asynchronous calls.

const list = List.of('/users.json', '/tasks.json');
const mapped = await list
  .mapAsync(fetch)
  .mapAsync(res => res.json());
//=> List [ <users_json_data>, <tasks_json_data> ]

#mapLimit()

List#mapLimit :: List<T> ~> (T -> Promise<T> | T, Number) -> Promise<List<T>>

Asynchronously maps a List into a new List by using a provided asynchronous mapper. Similar to List#mapAsync(), but only the provided limit of mappers can be run in parallel. If the given limit of mappers are already running, but we have not reached the end of the List, then any following mappers will be queued up to begin whenever a previous mapper completes.

For example, the following code will collect users and tasks data in parallel, but products collection will not begin until one of either users or tasks has completed.

const list = List.of('/users.json', '/tasks.json', '/products.json');
const limit = 2;
const mapped = await list.mapLimit(async elem => {
  const response = await fetch(elem);
  const json = await response.json();
  return json;
}, limit);
//=> List [ <users_json_data>, <tasks_json_data>, <products_json_data> ]

#mapSeries()

List#mapSeries :: List<T> ~> (T -> Promise<T> | T) -> Promise<List<T>>

Asynchronously maps a List into a new Listby using a provided asynchronousmapper. This is similar to List#mapAsync()`, but all asynchronous mapping will be executed in series, instead of in parallel.

const list = List.of('/users.json', '/tasks.json');
const mapped = await list.mapSeries(async elem => {
  const response = await fetch(elem);
  const json = await response.json();
  return json;
});
//=> List [ <users_json_data>, <tasks_json_data> ]

#none()

List#none :: List<T> ~> (T -> Boolean) -> Boolean
List#none :: List<T> ~> () -> Boolean

Returns a boolean indicating whether or not none of the elements in the List passed a given predicate.

const list = List.of(1, 2, '3', 4);
const none = list.none(x => typeof x === 'string');
//=> false

If no predicate is provided, then the identity function will be used as a default.

const list = List.of(null, undefined, 0, '', false);
const none = list.none();
//=> true

Using none() on an empty List will return true.

const list = List.of();
const none = list.none();
//=> true

#orderBy()

List#orderBy :: List<T> ~> (...Selector[]) -> List<T>
Selector :: String | Function | Array<String, Number> | Array<Function, Number>

Returns a new List, ordered by using a stable sort built from the provided selector definitions.

let unordered = new List(
  { id: 1, name: 'bob', data: 123 },
  { id: 2, name: 'bob', data: 422 },
  { id: 3, name: 'jim', data: 421 },
  { id: 4, name: 'bob', data: 321 },
  { id: 5, name: 'jim', data: 421 },
  { id: 6, name: 'jim', data: 123 },
  { id: 7, name: 'bob', data: 421 },
  { id: 8, name: 'jim', data: 123 }
);
let ordered = unordered.orderBy('name', 'data');
/*=> List [
  { id: 1, name: 'bob', data: 123 },
  { id: 4, name: 'bob', data: 321 },
  { id: 7, name: 'bob', data: 421 },
  { id: 2, name: 'bob', data: 422 },
  { id: 6, name: 'jim', data: 123 },
  { id: 8, name: 'jim', data: 123 },
  { id: 3, name: 'jim', data: 421 },
  { id: 5, name: 'jim', data: 421 }
] */

Selectors can be provided as strings, functions, or arrays containing a string or function and direction.

If a selector is provided as a string, it will use the equivalent of x => x[string].

If a selector is provided as a function, it will be used as is.

If provided as an array, it will use the first element as the selector, and the second element as the sort direction.

Note that in the case of providing a selector as a string, you may also prepend a '-' to the beginning of the string to indicate that you wish to use a descending sort for that selector.

For example, the following are all functionally equivalent, and List#orderBy will sort first by name ascending, then by data descending.

list.orderBy('name', '-data');
list.orderBy(x => x.name, [ x => x.data, -1 ]);
list.orderBy('name', x => x.data * -1);
list.orderBy([ 'name', 1 ], [ 'data', -1 ]);

#reduce()

List#reduce :: List<T> ~> ((U, T, Number, List<T>) -> U, U) -> U
List#reduce :: List<T> ~> ((U, T, Number, List<T>) -> U) -> U

Reduces a List into a single value by iterating over and executing the provided reducer for each value, optionally starting with a given seed value.

const list = List.of(2, 3, 4, 5);
const reduced = List.reduce((accumulator, elem) => {
  return accumulator + elem;
}, 1);
//=> 15 

If no seed value is provided, then the first element of the List will be used as the seed.

const list = List.of(1, 2, 3, 4);
const reduced = List.reduce((accumulator, elem) => accumulator + elem);
//=> 10

The index of the element will be provided to the given reducer.

const list = List.of(1, 2, 3, 4);
const reduced = List.reduce((accumulator, elem, index) => {
  return accumulator + index;
}, 0);
//=> 6

A reference to the original List will also be provided to the given reducer.

const list = List.of(1, 2, 3, 4);
const reduced = List.reduce((accumulator, elem, index, listRef) => {
  console.log(list === listRef); //=> true
});

#resolve()

List#resolve :: List<Promise<T> | T> ~> () -> Promise<List<T>>

A helper method for resolving a List of promises or values down into a List of values wrapped in a single promise.

const list = List.of(1, Promise.resolve(2), 3);
const resolved = await list.resolve();
//=> List [ 1, 2, 3 ]

Calling this method is the equivalent of calling Promise.all(list).then(List.from).

#reversed()

List#reversed :: List<T> ~> () -> List<T>

Returns a List with elements in the reverse order. Note that this method does not modify the original List, unlike Array#reverse().

const list = List.of(1, 2, 3);
const reversed = list.reversed();
//=> List [ 3, 2, 1 ]

#sum()

List#sum :: List<Number> ~> () -> Number
List#sum :: List<T> ~> ((T) -> Number) -> Number

Returns the sum of all elements in the List.

const list = List.of(1, 2, 3);
const sum = list.sum();
//=> 6

Accepts an optional selector to determine what elements to sum.

const list = List.of(
  { id: 1, count: 3 },
  { id: 2, count: 2 },
  { id: 3, count: 4 },
  { id: 4, count: 1 }
);
const sum = list.sum(elem => elem.count);
//=> 10

#tail()

List::#tail :: List<T> ~> () -> List<T>

Returns a List containing every element but the first from the original.

const list = List.of(1, 2, 3);
const tail = list.tail();
//=> List [ 2, 3 ]

#take()

List#take :: List<T> ~> (Number) -> List<T>

Returns a List containing a given number of elements.

const list = List.of(1, 2, 3, 4, 5);
const taken = list.take(3);
//=> List [ 1, 2, 3 ]

If the given number of elements to take is larger than the number of available elements, then the entire list will be taken.

const list = List.of(1, 2, 3);
const taken = list.take(4);
//=> List [ 1, 2, 3, 4 ]

#takeWhile()

List#takeWhile :: List<T> ~> (T -> Boolean) -> List<T>

Takes elements from the front of a List which pass a given predicate. Iteration will stop as soon as one element fails the given predicate.

const list = List.of(1, 2, 3, 2, 1);
const taken = list.takeWhile(x => x < 3);
//=> List [ 1, 2 ]

#unique()

List#unique :: List<T> ~> () -> List<T>
List#unique :: List<T> ~> (T -> String | Number) -> List<T>

Returns a List containing only unique elements from the original list.

const list = List.of(1, 2, 3, 3, 4, 4, 4, 5);
const unique = list.unique();
//=> List [ 1, 2, 3, 4, 5 ]

Unique accepts an optional hashing function used to determine the uniqueness of the elements.

const list = List.of(
  { id: 1, value: 3 },
  { id: 2, value: 4 },
  { id: 3, value: 3 },
  { id: 4, value: 2 }
);
const unique = list.unique(x => x.value);
/*=> List [
  { id: 1, value: 3 },
  { id: 2, value: 4 },
  { id: 4, value: 2 }
] */

Note that the first unique element found will be the one placed in the resulting List.

About

A functional, performant, list processing library designed to take advantage of Array subclassing.

Resources

License

Stars

Watchers

Forks

Packages

No packages published