UNPKG

sequins

Version:

Mutable sequences and native data structures (Map, Set, List) following the Immutable.js API

1,760 lines (1,573 loc) 70.7 kB
/** * The Immutable.js API is copyrighted (c) 2014-present by Facebook, Inc. * It is used here by permission of the MIT license, and under the principle * of fair use. * * @grouped */ import { NativeMapConstructor, NativeSetConstructor, NativeMap, NativeSet } from "./native"; type CollectionLike<K, V> = | Collection<K, V> | Array<V> | NativeSet<V> | NativeMap<K, V>; // Concrete interface ListConstructor { new <T>(collection: Iterable<T>): List<T>; new (collection?: null): List<any>; new <T>(): List<T>; /** * True if the provided value is a List * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * List.isList([]); // false * List.isList(new List()); // true * ``` */ isList(maybeList: any): maybeList is List<any>; /** * Creates a new Sequins List containing `values`. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * List.of(1, 2, 3, 4) * // List [ 1, 2, 3, 4 ] * ``` * * Note: Values are not altered or converted in any way. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * List.of({x:1}, 2, [3], 4) * // List [ { x: 1 }, 2, [ 3 ], 4 ] * ``` */ of<T>(...values: Array<T>): List<T>; /** * @ignore */ readonly prototype: List<any>; } /** * List is a dense `Indexed` `Collection` backed by a JavaScript array. * List shares its peformance charactersitics with array too. `get`, `set`, * `push`, and `pop` are all O(1) on lists. `shift` and `unshift` are O(n). * * Unlike a JavaScript Array, there is no distinction between an * "unset" index and an index set to `undefined`. `List#forEach` visits all * indices from 0 to size, regardless of whether they were explicitly defined. */ interface List<T> extends Concrete<number, T>, Indexed<T> { /** * The number of items in this List. */ readonly size: number; // Persistent changes /** * Sets `index` to `value`. * * `index` may be a negative number, which indexes back from the end of the * List. `v.set(-1, "value")` sets the last item in the List. * * If `index` larger than `size`, the returned List's `size` will be large * enough to include the `index`. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * const originalList = List([ 0 ]); * // List [ 0 ] * originalList.set(1, 1); * // List [ 0, 1 ] * originalList.set(0, 'overwritten'); * // List [ "overwritten" ] * originalList.set(2, 2); * // List [ 0, undefined, 2 ] * * new List().set(50000, 'value').size; * // 50001 * ``` */ set(index: number, value: T): List<T>; /** * Removes the value at `index` from the list. Values at indices above * `index` are shifted down by 1 to fill the position. * * This is synonymous with `list.splice(index, 1)`. * * `index` may be a negative number, which indexes back from the end of the * List. `v.delete(-1)` deletes the last item in the List. * * Note: `delete` cannot be safely used in IE8 * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new List([ 0, 1, 2, 3, 4 ]).delete(0); * // List [ 1, 2, 3, 4 ] * ``` * * @alias remove */ delete(index: number): List<T>; remove(index: number): List<T>; /** * Inserts `value` at `index` in the list. Values at indices above * `index` are shifted over by 1. * * This is synonymous with `list.splice(index, 0, value)`. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new List([ 0, 1, 2, 3, 4 ]).insert(6, 5) * // List [ 0, 1, 2, 3, 4, 5 ] * ``` */ insert(index: number, value: T): List<T>; /** * Removes all values from the list. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new List([ 1, 2, 3, 4 ]).clear() * // List [] * ``` */ clear(): List<T>; /** * Appends `values` to the end of the list. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new List([ 1, 2, 3, 4 ]).push(5) * // List [ 1, 2, 3, 4, 5 ] * ``` */ push(...values: Array<T>): List<T>; /** * Removes the last value from the list and returns it. * * ```js * new List([ 1, 2, 3, 4 ]).pop() * // List[ 1, 2, 3 ] * ``` */ pop(): List<T>; /** * Inserts `values` at the beginning of the list. Note that this will * require shifting every item in the list, and will take O(n) time. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new List([ 2, 3, 4]).unshift(1); * // List [ 1, 2, 3, 4 ] * ``` */ unshift(...values: Array<T>): List<T>; /** * Removes the first value from the list and returns it. Note that * this will require shifting every item in the list, and will take O(n) * time. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new List([ 0, 1, 2, 3, 4 ]).shift(); * // List [ 1, 2, 3, 4 ] * ``` */ shift(): List<T>; /** * Sets list's `size`. If `size` is less than the list's size, values at * higher indices will be excluded. If `size` is greater than the list's * size, newly created indicies will have undefined values. */ setSize(size: number): List<T>; // Sequence algorithms /** * Returns a new List with other values or collections concatenated to this one. * * @alias merge */ concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): List<T | C>; merge<C>(...collections: Array<Iterable<C>>): List<T | C>; /** * Returns a new List with values passed through a * `mapper` function. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new List([ 1, 2 ]).map(x => 10 * x) * // List [ 10, 20 ] * ``` */ map<M>(mapper: (value: T, key: number, iter: this) => M): List<M>; /** * @see Collection.flatten */ flatten(depth?: number): List<any>; flatten(shallow?: boolean): List<any>; /** * Flat-maps the List, returning a new List. * * Similar to `list.map(...).flatten(true)`. */ flatMap<M>( mapper: (value: T, key: number, iter: this) => Iterable<M> ): List<M>; /** * Returns a new List with only the values for which the `predicate` * function returns true. */ filter<F extends T>( predicate: (value: T, index: number, iter: this) => value is F ): List<F>; filter(predicate: (value: T, index: number, iter: this) => any): this; /** * Returns a List "zipped" with the provided collection. * * Like `zipWith`, but using the default `zipper`: creating an `Array`. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * const a = new List([ 1, 2, 3 ]); * const b = new List([ 4, 5, 6 ]); * const c = a.zip(b); // List [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ] * ``` */ zip<U>(other: CollectionLike<any, U>): List<[T, U]>; zip<U, V>( other: CollectionLike<any, U>, other2: CollectionLike<any, V> ): List<[T, U, V]>; zip(...collections: Array<CollectionLike<any, any>>): List<any>; /** * Returns a List "zipped" with the provided collections. * * Unlike `zip`, `zipAll` continues zipping until the longest collection is * exhausted. Missing values from shorter collections are filled with `undefined`. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * const a = new List([ 1, 2 ]); * const b = new List([ 3, 4, 5 ]); * const c = a.zipAll(b); // List [ [ 1, 3 ], [ 2, 4 ], [ undefined, 5 ] ] * ``` * * Note: Since zipAll will return a collection as large as the largest * input, some results may contain undefined values. TypeScript cannot * account for these without cases (as of v2.5). */ zipAll<U>(other: CollectionLike<any, U>): List<[T, U]>; zipAll<U, V>( other: CollectionLike<any, U>, other2: CollectionLike<any, V> ): List<[T, U, V]>; zipAll(...collections: Array<CollectionLike<any, any>>): List<any>; /** * Returns a List "zipped" with the provided collections by using a * custom `zipper` function. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * const a = new List([ 1, 2, 3 ]); * const b = new List([ 4, 5, 6 ]); * const c = a.zipWith((a, b) => a + b, b); * // List [ 5, 7, 9 ] * ``` */ zipWith<U, Z>( zipper: (value: T, otherValue: U) => Z, otherCollection: CollectionLike<any, U> ): List<Z>; zipWith<U, V, Z>( zipper: (value: T, otherValue: U, thirdValue: V) => Z, otherCollection: CollectionLike<any, U>, thirdCollection: CollectionLike<any, V> ): List<Z>; zipWith<Z>( zipper: (...any: Array<any>) => Z, ...collections: Array<CollectionLike<any, any>> ): List<Z>; // Combination /** * Returns a List with `separator` between each item. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new List([ 1, 2, 3 ]).interpose(null); * // List [ 1, null, 2, null, 3] * ``` */ interpose<S>(separator: S): List<S | T>; // Conversions /** * Deeply converts all nested structures to Objects and Arrays. */ toJS(): Array<any>; /** * Returns an Array containing the values from the List. */ toJSON(): Array<T>; toConcrete(): this; /** * Returns an `IndexedSequence` of the values from the List. */ toSeq(): IndexedSequence<T>; } export const List: ListConstructor; interface MapConstructor { /** * This signature is just to make typescript happy about type inference with * literal arrays of arrays. Technically the Iterable signature already * covers the Array case, so no need to complicate the docs with an * implementation detail. * @ignore */ new <K, V>(entries: ReadonlyArray<[K, V]>): Map<K, V>; new <K, V>(entries: Iterable<[K, V]>): Map<K, V>; new <K, V>(collection: Collection<K, V>): Map<K, V>; new <V>(obj: { [key: string]: V }): Map<string, V>; new (entries?: null): Map<any, any>; new <K, V>(): Map<K, V>; /** * True if the provided value is a Map * * <!-- runkit:activate * { "preamble": "const { Map } = require('sequins');" } * --> * ```js * Map.isMap({}) // false * Map.isMap(new Map()) // true * ``` */ isMap(maybeMap: any): maybeMap is Map<any, any>; /** * @ignore */ readonly prototype: Map<any, any>; } /** * Map is a `Keyed` `Collection` of `[key, value]` tuples with * O(1) `get` and `set`. Its API is fully compatible with that of * [JavaScript Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), * but the Sequins Map class delegates to a native Map for storage as * opposed to extending the native Map class. * * Map's keys can be of any type. This allows the use of any value * (including NaN) as a key. Strict identity is used to evaluate key * equality. Two similar looking objects, for example, when both used as * keys, will store two separate values. */ interface Map<K, V> extends Concrete<K, V>, Keyed<K, V> { /** * The number of entries in this Map. */ readonly size: number; // Persistent changes /** * Sets `key` to `value`. If an equivalent the key already exists in * the map, it will be replaced. * * <!-- runkit:activate * { "preamble": "const { Map } = require('sequins');" } * --> * ```js * const originalMap = Map() * const newerMap = originalMap.set('key', 'value') * const newestMap = newerMap.set('key', 'newer value') * * originalMap * // Map {} * newerMap * // Map { "key": "value" } * newestMap * // Map { "key": "newer value" } * ``` */ set(key: K, value: V): this; /** * Removes `key` and its associated value from the map. * * Note: `delete` cannot be safely used in IE8, but is provided to mirror * the ES6 collection API. * * <!-- runkit:activate * { "preamble": "const { Map } = require('sequins');" } * --> * ```js * const originalMap = Map({ * key: 'value', * otherKey: 'other value' * }) * // Map { "key": "value", "otherKey": "other value" } * originalMap.delete('otherKey') * // Map { "key": "value" } * ``` * * @alias remove */ delete(key: K): this; remove(key: K): this; /** * Removes all keys and values from the map. * * <!-- runkit:activate * { "preamble": "const { Map } = require('sequins');" } * --> * ```js * new Map({ key: 'value' }).clear() * // Map {} * ``` */ clear(): this; /** * Returns a new Map resulting from merging the provided Collections * (or JS objects) into this Map. In other words, this takes each entry of * each collection and sets it on this Map. * * Note: Values provided to `merge` are shallowly converted before being * merged. No nested values are altered. * * <!-- runkit:activate * { "preamble": "const { Map } = require('sequins');" } * --> * ```js * const one = new Map({ a: 10, b: 20, c: 30 }) * const two = new Map({ b: 40, a: 50, d: 60 }) * one.merge(two) // Map { "a": 50, "b": 40, "c": 30, "d": 60 } * two.merge(one) // Map { "b": 20, "a": 10, "d": 60, "c": 30 } * ``` * * @alias concat */ merge<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): Map<K | KC, V | VC>; merge<C>(...collections: Array<{ [key: string]: C }>): Map<K | string, V | C>; concat<KC, VC>( ...collections: Array<Iterable<[KC, VC]>> ): Map<K | KC, V | VC>; concat<C>( ...collections: Array<{ [key: string]: C }> ): Map<K | string, V | C>; // Sequence algorithms /** * Returns a new Map with values passed through a * `mapper` function. * * new Map({ a: 1, b: 2 }).map(x => 10 * x) * // Map { a: 10, b: 20 } */ map<M>(mapper: (value: V, key: K, iter: this) => M): Map<K, M>; /** * @see Keyed.mapKeys */ mapKeys<M>(mapper: (key: K, value: V, iter: this) => M): Map<M, V>; /** * @see Keyed.mapEntries */ mapEntries<KM, VM>( mapper: (entry: [K, V], index: number, iter: this) => [KM, VM] ): Map<KM, VM>; /** * @see Collection.flatten */ flatten(depth?: number): Map<any, any>; flatten(shallow?: boolean): Map<any, any>; /** * Flat-maps the Map, returning a new Map. * * Similar to `data.map(...).flatten(true)`. */ flatMap<KM, VM>( mapper: (value: V, key: K, iter: this) => Iterable<[KM, VM]> ): Map<KM, VM>; /** * Returns a new Map with only the entries for which the `predicate` * function returns true. */ filter<F extends V>( predicate: (value: V, key: K, iter: this) => value is F ): Map<K, F>; filter(predicate: (value: V, key: K, iter: this) => any): this; /** * @see Keyed.flip */ flip(): Map<V, K>; // Conversions /** * Deeply converts all nested structures to Objects and Arrays. */ toJS(): { [key: string]: any }; /** * Returns an Object with the keys (stringified) and values from the Map. */ toJSON(): { [key: string]: V }; toConcrete(): this; /** * Returns an `KeyedSequence` of the entries from the List. */ toSeq(): KeyedSequence<K, V>; } export const Map: MapConstructor; interface SetConstructor { new <T>(collection: Iterable<T>): Set<T>; new (collection?: null): Set<any>; new <T>(): Set<T>; /** * True if the provided value is a Set */ isSet(maybeSet: any): maybeSet is Set<any>; /** * Creates a new Set containing `values`. */ of<T>(...values: Array<T>): Set<T>; /** * `Set.fromKeys()` creates a new immutable Set containing the keys from * this Collection or JavaScript Object. */ fromKeys<T>(iter: Collection<T, any>): Set<T>; fromKeys(obj: { [key: string]: any }): Set<string>; fromKeys(): Set<any>; /** * `Set.union()` creates a new Set that includes all members present in any * input Set. * * If an input is an associative iterable, its values are considered the Set. * * ```js * const { Set } = require('immutable') * const unioned = Set.union([ * Set([ 'a', 'b', 'c' ]) * Set([ 'c', 'a', 't' ]) * ]) * // Set [ "a", "b", "c", "t"" ] * ``` */ union<T>(...sets: Array<Iterable<T>>): Set<T>; /** * `Set.intersect()` creates a new Set that includes only members that are * present in all input Sets. * * If an input is an associative iterable, its values are considered the Set. * * ```js * const { Set } = require('immutable') * const intersected = Set.intersect([ * Set([ 'a', 'b', 'c' ]) * Set([ 'c', 'a', 't' ]) * ]) * // Set [ "a", "c"" ] * ``` */ intersect<T>(...sets: Array<Iterable<T>>): Set<T>; /** * @ignore */ readonly prototype: Set<any>; } /** * Set is a `Duplicated` `Collection` of unique values with O(1) `add` and `has`. * Its API is fully compatible with that of * [JavaScript Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set), * but the Sequins Set class delegates to a native Set for storage as * opposed to extending the native Set class. * * When iterating a Set, the entries will be [value, value] tuples. * * Set values, like Map keys, may be of any type including other collections * and NaN. */ interface Set<T> extends Concrete<T, T>, Duplicated<T> { /** * The number of items in this Set. */ readonly size: number; // Persistent changes /** * Adds value to the set. */ add(value: T): this; /** * Removes value from the set. * * Note: `delete` **cannot** be safely used in IE8, use `remove` if * supporting old browsers. * * @alias remove */ delete(value: T): this; remove(value: T): this; /** * Clears all keys and values from the set */ clear(): this; /** * Returns a Set including any value from `collections` that does not already * exist in this Set. * @alias merge * @alias concat */ union<C>(...collections: Array<Iterable<C>>): Set<T | C>; merge<C>(...collections: Array<Iterable<C>>): Set<T | C>; concat<C>(...collections: Array<Iterable<C>>): Set<T | C>; /** * Returns a Set which has removed any values not also contained * within `collections`. */ intersect(...collections: Array<Iterable<T>>): this; /** * Returns a Set excluding any values contained within `collections`. * * <!-- runkit:activate * { "preamble": "const { Set } = require('sequins');" } * --> * ```js * new Set([ 1, 2, 3 ]).subtract([1, 3]) * // Set [2] * ``` */ subtract(...collections: Array<Iterable<T>>): this; // Sequence algorithms /** * Returns a new Set with values passed through a * `mapper` function. * * new Set([1,2]).map(x => 10 * x) * // Set [10,20] */ map<M>(mapper: (value: T, key: T, iter: this) => M): Set<M>; /** * @see Collection.flatten */ flatten(depth?: number): Set<any>; flatten(shallow?: boolean): Set<any>; /** * Flat-maps the Set, returning a new Set. * * Similar to `set.map(...).flatten(true)`. */ flatMap<M>(mapper: (value: T, key: T, iter: this) => Iterable<M>): Set<M>; /** * Returns a new Set with only the values for which the `predicate` * function returns true. */ filter<F extends T>( predicate: (value: T, key: T, iter: this) => value is F ): Set<F>; filter(predicate: (value: T, key: T, iter: this) => any): this; // Conversions /** * Deeply converts all nested structures to Objects and Arrays. */ toJS(): Array<any>; /** * Returns an Array with the values from the Set. */ toJSON(): Array<T>; toConcrete(): this; /** * Returns a `SetSequence` of the values from the Set. */ toSeq(): SetSequence<T>; } export const Set: SetConstructor; // Sequence interface IndexedSequenceConstructor { new <T>(collection: Iterable<T>): IndexedSequence<T>; new (collection?: null): IndexedSequence<any>; new <T>(): IndexedSequence<T>; /** * Creates a new IndexedSequence containing `values`. */ of<T>(...values: Array<T>): IndexedSequence<T>; /** * @ignore */ readonly prototype: IndexedSequence<any>; } /** * An IndexedSequence is quite simply an `Indexed` `Sequence`. It represents * sequential transformations against Array or List-like data as a series of * chained function calls. The chain of calls will usually be terminated with * `toList`. * * When constructing an IndexedSequence from another data type, keys will be * discarded. */ interface IndexedSequence<T> extends Sequence<number, T>, Indexed<T> { // Combination /** * Returns a new IndexedSequence with other collections concatenated to this * one. */ concat<C>( ...valuesOrCollections: Array<Iterable<C> | C> ): IndexedSequence<T | C>; /** * Returns an IndexedSequence with `separator` between each item. * * <!-- runkit:activate * { "preamble": "const { List } = require('sequins');" } * --> * ```js * new IndexedSequence([ 1, 2, 3 ]).interpose(null); * // IndexedSequence [ 1, null, 2, null, 3] * ``` */ interpose<S>(separator: S): IndexedSequence<S | T>; // Sequence algorithms /** * Returns a new IndexedSequence with values passed through a * `mapper` function. * * <!-- runkit:activate * { "preamble": "const { Seq } = require('sequins');" } * --> * ```js * IndexedSequence([ 1, 2 ]).map(x => 10 * x) * // Seq [ 10, 20 ] * ``` */ map<M>(mapper: (value: T, key: number) => M): IndexedSequence<M>; /** * Does not alter the sequence, but allows you to inspect values as they are * comptued. Returns the sequence for chaining. Unlike `forEach`, tap does not * evaluate the sequence. * * ```js * const seq = IndexedSequence([ 1, 2 ]).tap(x => console.log(x)) * Array.from(seq); // logs 1, 2 * ``` */ tap(fn: (value: T, key: number) => any): this; /** * @see Collection.flatten */ flatten(depth?: number): IndexedSequence<any>; flatten(shallow?: boolean): IndexedSequence<any>; /** * Flat-maps the IndexedSequence, returning an IndexedSequence. * * Similar to `seq.map(...).flatten(true)`. */ flatMap<M>( mapper: (value: T, key: number) => Iterable<M> ): IndexedSequence<M>; /** * Returns a new IndexedSequence with only the values for which the * `predicate` function returns true. */ filter<F extends T>( predicate: (value: T, index: number) => value is F ): IndexedSequence<F>; filter(predicate: (value: T, index: number) => any): this; /** * Returns a new IndexedSequence "zipped" with the provided collections. * * Like `zipWith`, but using the default `zipper`: creating an `Array`. * * ```js * const a = Seq([ 1, 2, 3 ]); * const b = Seq([ 4, 5, 6 ]); * const c = a.zip(b); // Seq [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ] * ``` */ zip<U>(other: CollectionLike<any, U>): IndexedSequence<[T, U]>; zip<U, V>( other: CollectionLike<any, U>, other2: CollectionLike<any, V> ): IndexedSequence<[T, U, V]>; zip(...collections: Array<CollectionLike<any, any>>): IndexedSequence<any>; /** * Returns a new IndexedSequence "zipped" with the provided collections. * Continues until the longest collection is exhausted. * * Missing values from shorter collections are filled with `undefined`. * * ```js * const a = Seq([ 1, 2 ]); * const b = Seq([ 3, 4, 5 ]); * const c = a.zipAll(b); // Seq [ [ 1, 3 ], [ 2, 4 ], [ undefined, 5 ] ] * ``` */ zipAll<U>(other: CollectionLike<any, U>): IndexedSequence<[T, U]>; zipAll<U, V>( other: CollectionLike<any, U>, other2: CollectionLike<any, V> ): IndexedSequence<[T, U, V]>; zipAll(...collections: Array<CollectionLike<any, any>>): IndexedSequence<any>; /** * Returns a new IndexedSequence "zipped" with the provided collections by * using a custom `zipper` function. * * ```js * const a = Seq([ 1, 2, 3 ]); * const b = Seq([ 4, 5, 6 ]); * const c = a.zipWith((a, b) => a + b, b); * // Seq [ 5, 7, 9 ] * ``` */ zipWith<U, Z>( zipper: (value: T, otherValue: U) => Z, otherCollection: CollectionLike<any, U> ): IndexedSequence<Z>; zipWith<U, V, Z>( zipper: (value: T, otherValue: U, thirdValue: V) => Z, otherCollection: CollectionLike<any, U>, thirdCollection: CollectionLike<any, V> ): IndexedSequence<Z>; zipWith<Z>( zipper: (...any: Array<any>) => Z, ...collections: Array<CollectionLike<any, any>> ): IndexedSequence<Z>; // Conversions /** * Returns a `List` of the sequence's values */ toConcrete(): List<T>; /** * Returns itself */ toSeq(): this; /** * Deeply converts this IndexedSequence to equivalent native JavaScript * Array. */ toJS(): Array<any>; /** * Shallowly converts this IndexedSequence to equivalent native JavaScript * Array. */ toJSON(): Array<T>; } export const IndexedSequence: IndexedSequenceConstructor; interface KeyedSequenceConstructor { /** * This signature is just to make typescript happy about type inference with * literal arrays of arrays. Technically the Iterable signature already * covers the Array case, so no need to complicate the docs with an * implementation detail. * @ignore */ new <K, V>(entries: ReadonlyArray<[K, V]>): KeyedSequence<K, V>; new <K, V>(entries: Iterable<[K, V]>): KeyedSequence<K, V>; new <K, V>(collection: Collection<K, V>): KeyedSequence<K, V>; new <V>(obj: { [key: string]: V }): KeyedSequence<string, V>; new (entries?: null): KeyedSequence<any, any>; new <K, V>(): KeyedSequence<K, V>; /** * @ignore */ readonly prototype: KeyedSequence<any, any>; } /** * A KeyedSequence is, as expected, a `Keyed` `Sequence`. It represents * sequential transformations on Object or Map-like data as a series of * chained function calls. Note that a KeyedSequence lacks the key-coalescing * property of a `Map`. Duplicate keys will be eliminated when calling `as(Map)` * which will usually be the last call in the chain. * * When constructing a KeyedSequence pass either keyed data or an iterable of * [K, V] tuples. */ interface KeyedSequence<K, V> extends Sequence<K, V>, Keyed<K, V> { /** * Returns a new KeyedSequence with other collections concatenated to this one. * * All entries will be present in the resulting KeyedSequence, even if they * have the same key. */ concat<KC, VC>( ...collections: Array<Iterable<[KC, VC]>> ): KeyedSequence<K | KC, V | VC>; concat<C>( ...collections: Array<{ [key: string]: C }> ): KeyedSequence<K | string, V | C>; /** * Returns a new KeyedSequence with values passed through a * `mapper` function. * * <!-- runkit:activate * { "preamble": "const { KeyedSequence } = require('sequins');" } * --> * ```js * KeyedSequence({ a: 1, b: 2 }).map(x => 10 * x) * // Seq { "a": 10, "b": 20 } * ``` */ map<M>(mapper: (value: V, key: K) => M): KeyedSequence<K, M>; /** * @see Keyed.mapKeys */ mapKeys<M>(mapper: (key: K, value: V) => M): KeyedSequence<M, V>; /** * @see Keyed.mapEntries */ mapEntries<KM, VM>( mapper: (entry: [K, V], index: number) => [KM, VM] ): KeyedSequence<KM, VM>; /** * Does not alter the sequence, but allows you to inspect keys and values as * they are comptued. Returns the sequence for chaining. Unlike `forEach`, tap * does not evaluate the sequence. * * ```js * const seq = KeyedSequence([[1, 1], [2, 2]]).tap(x => console.log(x)) * Array.from(seq); // logs 1, 2 * ``` */ tap(fn: (value: V, key: K) => any): KeyedSequence<K, V>; /** * @see Collection.flatten */ flatten(depth?: number): KeyedSequence<any, any>; flatten(shallow?: boolean): KeyedSequence<any, any>; /** * Flat-maps the KeyedSequence, returning a new KeyedSequence. * * Similar to `seq.map(...).flatten(true)`. */ flatMap<KM, VM>( mapper: (value: V, key: K) => Iterable<[KM, VM]> ): KeyedSequence<KM, VM>; /** * Returns a new KeyedSequence with only the entries for which the `predicate` * function returns true. */ filter<F extends V>( predicate: (value: V, key: K) => value is F ): KeyedSequence<K, F>; filter(predicate: (value: V, key: K) => any): this; /** * @see Keyed.flip */ flip(): KeyedSequence<V, K>; // Conversions /** * Deeply converts this KeyedSequence to equivalent native JavaScript Object. * * Converts keys to Strings. */ toJS(): Object; /** * Shallowly converts this KeyedSequence to equivalent native JavaScript Object. * * Converts keys to Strings. */ toJSON(): { [key: string]: V }; /** * Returns a `Map` of the entries from this sequence. */ toConcrete(): Map<K, V>; /** * Returns itself */ toSeq(): this; } export const KeyedSequence: KeyedSequenceConstructor; interface SetSequenceConstructor { new <T>(collection: Iterable<T>): SetSequence<T>; new (collection?: null): SetSequence<any>; new <T>(): SetSequence<T>; /** * Creates a new SetSequence containing `values`. */ of<T>(...values: Array<T>): SetSequence<T>; /** * @ignore */ readonly prototype: SetSequence<any>; } /** * A SetSequence is a `Duplicated` `Sequence`. It represents sequntial * transformations on Set-like data as a series of chained function calls. * Note that a SetSequence is allowed to contain duplicate values. Such * duplicates will be eliminated when using `as(Set)` or a similar method to * convert back to a concrete type after the desired transformations are * made. * * When constructing a SetSequence from another data type, any associated * indices or keys are discareded. */ interface SetSequence<T> extends Sequence<T, T>, Duplicated<T> { /** * Returns a new SetSequence with other collections concatenated to this one. * * All entries will be present in the resulting SetSequence, even if they * are duplicates. */ concat<U>(...collections: Array<Iterable<U>>): SetSequence<T | U>; /** * Returns a new SetSequence with values passed through a * `mapper` function. * * ```js * SetSequence([ 1, 2 ]).map(x => 10 * x) * // Seq { 10, 20 } * ``` */ map<M>(mapper: (value: T, key: T) => M): SetSequence<M>; /** * Does not alter the sequence, but allows you to inspect values as they are * comptued. Returns the sequence for chaining. Unlike `forEach`, tap does not * evaluate the sequence. * * ```js * const seq = SetSequence([ 1, 2 ]).tap(x => console.log(x)) * Array.from(seq); // logs 1, 2 * ``` */ tap(fn: (value: T, key: T) => any): this; /** * @see Collection.flatten */ flatten(depth?: number): SetSequence<any>; flatten(shallow?: boolean): SetSequence<any>; /** * Flat-maps the SetSequence, returning a SetSequence. * * Similar to `seq.map(...).flatten(true)`. */ flatMap<M>(mapper: (value: T, key: T) => Iterable<M>): SetSequence<M>; /** * Returns a new SetSequence with only the values for which the `predicate` * function returns true. */ filter<F extends T>( predicate: (value: T, key: T) => value is F ): SetSequence<F>; filter(predicate: (value: T, key: T) => any): this; // Conversion /** * Deeply converts this SetSequence to equivalent native JavaScript Array. */ toJS(): Array<any>; /** * Shallowly converts this SetSequence to equivalent native JavaScript Array. */ toJSON(): Array<T>; /** * Returns a `Set` of the values from this sequence. */ toConcrete(): Set<T>; /** * Returns itself */ toSeq(): this; } export const SetSequence: SetSequenceConstructor; export function Seq<S extends Sequence<any, any>>(seq: S): S; export function Seq<K, V>(collection: Keyed<K, V>): KeyedSequence<K, V>; export function Seq<T>(collection: Indexed<T>): IndexedSequence<T>; export function Seq<T>(collection: Duplicated<T>): SetSequence<T>; export function Seq<T>(collection: Iterable<T>): IndexedSequence<T>; export function Seq<V>(obj: { [key: string]: V }): KeyedSequence<string, V>; export function Seq(collection?: null): IndexedSequence<any>; /** * Seq is a helper function for creating instances of `Sequence`. Given any argument, * Seq will make a best-effort guess as to the appropriate `Sequence` subtype, and * will return an instance of it. The desired Sequence type can be selected by using * one of the nested functions: `Seq.Indexed`, `Seq.Keyed`, or `Seq.Set`. * * Seq's best-effort selection of subtypes uses the following logic: * * * If a `Collection` a `Sequence` of the same subtype as the collection * * If an Array-like or Iterable, an `IndexedSequence`. * * If an Object, a `KeyedSequence`. * * @constructs */ export namespace Seq { /** * True if `maybeSeq` is a Sequence, it is not backed by a concrete * structure such as Map, List, or Set. */ function isSeq( maybeSeq: any ): maybeSeq is | IndexedSequence<any> | KeyedSequence<any, any> | SetSequence<any>; /** * Factory function for convenient construction of `KeyedSequence` instances * * @constructs */ export function Keyed<K, V>( collection: Iterable<[K, V]> ): KeyedSequence<K, V>; export function Keyed<V>(obj: { [key: string]: V }): KeyedSequence<string, V>; export function Keyed<K, V>(): KeyedSequence<K, V>; export function Keyed(): KeyedSequence<any, any>; export namespace Indexed { /** * Provides an IndexedSequence of the values provided. */ function of<T>(...values: Array<T>): IndexedSequence<T>; } /** * Factory function for convenient construction of `IndexedSequence` instances * * @constructs */ export function Indexed(): IndexedSequence<any>; export function Indexed<T>(): IndexedSequence<T>; export function Indexed<T>(collection: Iterable<T>): IndexedSequence<T>; export namespace Set { /** * Returns a SetSequence of the provided values */ function of<T>(...values: Array<T>): SetSequence<T>; } /** * Factory function for convenient construction of `SetSequence` instances * * @constructs */ export function Set(): SetSequence<any>; export function Set<T>(): SetSequence<T>; export function Set<T>(collection: Iterable<T>): SetSequence<T>; } // Native export const NativeMap: typeof NativeMapConstructor; export const NativeSet: typeof NativeSetConstructor; // Abstract /** * Concretes are a type of `Collection` which store their own data * and have O(1) random access. It is the base class for `List`, `Map`, and * `Set`. */ export interface Concrete<K, V> extends Collection<K, V> { // Reading values /** * Returns the value associated with the provided key, or notSetValue if * the Collection does not contain this key. * * Note: it is possible a key may be associated with an `undefined` value, * so if `notSetValue` is not provided and this method returns `undefined`, * that does not guarantee the key was not found. */ get<NSV>(key: K, notSetValue: NSV): V | NSV; get(key: K): V | undefined; /** * True if a key exists within this `Collection`. * to determine equality */ has(key: K): boolean; /** * True if a value exists within this `Collection`. * @alias contains */ includes(value: V): boolean; contains(value: V): boolean; /** * In case the `Collection` is not empty returns the first element of the * `Collection`. * In case the `Collection` is empty returns the optional default * value if provided, if no default value is provided returns undefined. */ first<NSV>(notSetValue?: NSV): V | NSV; // Reducing a value /** * Returns true if this Collection includes no values. * * For some lazy `Seq`, `isEmpty` might need to iterate to determine * emptiness. At most one iteration will occur. */ isEmpty(): boolean; } /** * Sequence is the abstract base class for describing efficient, lazy * transformations. Sequences never store their own data, instead they * describe how to compute values (or keys) using a series of transforms against * some source data. Sequences are immutable with regards to which transforms * they apply, but as their source data is mutable applying the sequence * transformations more than once may yield different results if the source data * has changed. Sequence subtypes are `IndexedSequence`, `KeyedSequence`, * and `SetSequence`. * * Why are Sequences efficient? Both a sequence and a concrete * structure may be transformed through chanined operations like * `nums.filter(x => x > 0).map(x => x + 1)`. The difference is in how they * execute. A concrete structure will do all the mapping (and store it) and * then do all the filtering on the mapped results. A sequence executes value * by value not transform by transform. The filter function will read numbers * until it finds one greater than 0 then the mapper function will * add one to it. Only then will a second value be read. * * This method of working gives sequences their laziness, because Sequence * values are consumed and operators applied to them only on demand. Values * generated by a sequence are not implicitly cached or stored. This means * that Sequences can even have infintely many items. Take for example this way * of expressing the concept of "the first n natural numbers:" * * <!-- runkit:activate * { "preamble": "const { Range } = require('sequins');" } * --> * ```js * function naturals(n) { * return Range(1, Infinity).slice(0, n).as(Array) * } * naturals(3); // [1, 2, 3] * ``` * We move smoothly past our inability to store a list of infinite size, * because slice will only ever request three values from Range. * * Because seuqences need not cache intermediate states, they shine when * working with multiple transforms on lists which may not be infinite but * are simply very large. In practical terms they allow the programmer to * reduce memory usage at the cost of CPU cycles. * * Finally, Sequence is often used to provide a rich collection API to JavaScript * Object. * * ```js * Seq({ x: 0, y: 1, z: 2 }).map(v => v * 2).as(Object); * // { x: 0, y: 2, z: 4 } * ``` */ export interface Sequence<K, V> extends Collection<K, V> { // Sequence algorithms /** * Returns a new Sequence with values passed through a * `mapper` function. * * <!-- runkit:activate * { "preamble": "const { Seq } = require('sequins');" } * --> * ```js * Seq([ 1, 2 ]).map(x => 10 * x) * // Seq [ 10, 20 ] * ``` */ map<M>(mapper: (value: V, key: K) => M): Sequence<K, M>; /** * Returns a new Sequence with values passed through a * `mapper` function. * * <!-- runkit:activate * { "preamble": "const { Seq } = require('sequins');" } * --> * ```js * Seq([ 1, 2 ]).map(x => 10 * x) * // Seq [ 10, 20 ] * ``` * * Note: used only for sets. */ map<M>(mapper: (value: V, key: K) => M): Sequence<M, M>; /** * Flat-maps the Sequence, returning a Sequence of the same type. * * Similar to `seq.map(...).flatten(true)`. */ flatMap<M>(mapper: (value: V, key: K) => Iterable<M>): Sequence<K, M>; flatMap<M>(mapper: (value: V, key: K) => Iterable<M>): Sequence<M, M>; /** * Returns a new Sequence with only the values for which the `predicate` * function returns true. */ filter<F extends V>( predicate: (value: V, key: K) => value is F ): Sequence<K, F>; filter(predicate: (value: V, key: K) => any): this; } /** * Collection is the base type for all Sequins structures. There are two * fundamental Collection types: Concrete and Sequence. The distinction is that * a `Concrete` stores its data, while a `Sequence` computes it. * Furthermore, all Collections have keys and values of some kind. Values are * similar everywhere, but the nature of keys is determined by the Collection's * subtype. Key may be explicitly declared ( `Keyed` subtype), the index of the * item in the collection ( `Indexed` subtype), or just the value again as a * placeholder ( `Duplicated` subtype). */ export interface Collection<K, V> { // Sequence algorithms /** * Returns a new Collection of the same type with values passed through a * `mapper` function. * * <!-- runkit:activate * { "preamble": "const { Collection } = require('sequins');" } * --> * ```js * Collection({ a: 1, b: 2 }).map(x => 10 * x) * // Sequence { "a": 10, "b": 20 } * ``` */ map<M>(mapper: (value: V, key: K, iter?: this) => M): Collection<K, M>; /** * Note: used only for sets, which return Collection<M, M> but are otherwise * identical to normal `map()`. * * @ignore */ map<M>(...args: never[]): any; /** * Returns a new Collection of the same type with only the entries for which * the `predicate` function returns true. * * <!-- runkit:activate * { "preamble": "const { Map } = require('sequins');" } * --> * ```js * new Map({ a: 1, b: 2, c: 3, d: 4}).filter(x => x % 2 === 0) * // Map { "b": 2, "d": 4 } * ``` */ filter<F extends V>( predicate: (value: V, key: K, iter?: this) => value is F ): Collection<K, F>; filter(predicate: (value: V, key: K, iter?: this) => any): this; /** * Returns a new Collection of the same type with only the entries for which * the `predicate` function returns false. * * <!-- runkit:activate * { "preamble": "const { Map } = require('sequins');" } * --> * ```js * new Map({ a: 1, b: 2, c: 3, d: 4}).filterNot(x => x % 2 === 0) * // Map { "a": 1, "c": 3 } * ``` */ filterNot(predicate: (value: V, key: K, iter?: this) => boolean): this; /** * Reverses the order of elements in the collection. */ reverse(): this; /** * Stably sorts the elements of the collection by using a `comparator`. * * If a `comparator` is not provided, a default comparator uses `<` and `>`. * * `comparator(valueA, valueB)`: * * * Returns `0` if the elements should not be swapped. * * Returns `-1` (or any negative number) if `valueA` comes before `valueB` * * Returns `1` (or any positive number) if `valueA` comes after `valueB` * * Is pure, i.e. it must always return the same value for the same pair * of values. * * When sorting collections which have no defined order, their ordered * equivalents will be returned. e.g. `map.sort()` returns OrderedMap. * * <!-- runkit:activate * { "preamble": "const { Map } = require('sequins');" } * --> * ```js * new Map({ "c": 3, "a": 1, "b": 2 }).sort((a, b) => { * if (a < b) { return -1; } * if (a > b) { return 1; } * if (a === b) { return 0; } * }); * // Map { "a": 1, "b": 2, "c": 3 } * ``` */ sort(comparator?: (valueA: V, valueB: V) => number): this; /** * Like `sort`, but also accepts a `comparatorValueMapper` which allows for * sorting by more sophisticated means: * * hitters.sortBy(hitter => hitter.avgHits) */ sortBy<C>( comparatorValueMapper: (value: V, key: K, iter?: this) => C, comparator?: (valueA: C, valueB: C) => number ): this; /** * Returns a `Keyed` of `Keyeds`, grouped by the return * value of the `grouper` function. * * <!-- runkit:activate * { "preamble": "const { List, Map } = require('sequins');" } * --> * ```js * const listOfMaps = new List([ * new Map({ v: 0 }), * new Map({ v: 1 }), * new Map({ v: 1 }), * new Map({ v: 0 }), * new Map({ v: 2 }) * ]) * const groupsOfMaps = listOfMaps.groupBy(x => x.get('v')) * // Map { * // 0: List [ new Map{ "v": 0 }, new Map { "v": 0 } ], * // 1: List [ new Map{ "v": 1 }, new Map { "v": 1 } ], * // 2: List [ new Map{ "v": 2 } ], * // } * ``` */ groupBy<G>( grouper: (value: V, key: K, iter?: this) => G ): /*Map*/ KeyedSequence<G, /*this*/ Collection<K, V>>; // Side effects /** * The `sideEffect` is executed for every entry in the Collection. * `forEach` has no return value! */ forEach(sideEffect: (value: V, key: K, iter?: this) => any): void; /** * The `sideEffect` is executed for entries in the Collection. * * If any call of `sideEffect` returns * `false`, the iteration will stop. Returns the number of entries iterated * (including the last iteration which returned false). */ forSome(sideEffect: (value: V, key: K, iter?: this) => any): number; // Creating subsets /** * Returns a new Collection of the same type representing a portion of this * Collection from start up to but not including end. * * If begin is negative, it is offset from the end of the Collection. e.g. * `slice(-2)` returns a Collection of the last two entries. If it is not * provided the new Collection will begin at the beginning of this Collection. * * If end is negative, it is offset from the end of the Collection. e.g. * `slice(0, -1)` returns a Collection of everything but the last entry. If * it is not provided, the new Collection will continue through the end of * this Collection. * * If the requested slice is equivalent to the current Collection, then it * will return itself. */ slice(begin?: number, end?: number): this; // Combination /** * Returns a new Collection of the same type with other values and * collection-like concatenated to this one. * * For Seqs, all entries will be present in the resulting Sequence, even if they * have the same key. */ concat(...valuesOrCollections: Array<any>): Collection<any, any>; /** * Flattens nested Collections. * * Will deeply flatten the Collection by default, returning a Collection of the * same type, but a `depth` can be provided in the form of a number or * boolean (where true means to shallowly flatten one level). A depth of 0 * (or shallow: false) will deeply flatten. * * Flattens only others Collection, not Arrays or Objects. * * Note: `flatten(true)` operates on Collection<any, Collection<K, V>> and * returns Collection<K, V> */ flatten(depth?: number): Collection<any, any>; flatten(shallow?: boolean): Collection<any, any>; /** * Flat-maps the Collection, returning a Collection of the same type. * * Similar to `collection.map(...).flatten(true)`. */ flatMap<M>( mapper: (value: V, key: K, iter?: this) => Iterable<M> ): Collection<K, M>; /** * Flat-maps the Collection, returning a Collection of the same type. * * Similar to `collection.map(...).flatten(true)`. * Used for Dictionaries only. */ flatMap<KM, VM>( mapper: (value: V, key: K, iter?: this) => Iterable<[KM, VM]> ): Collection<KM, VM>; // Reducing a value /** * Reduces the Collection to a value by calling the `reducer` for every entry * in the Collection and passing along the reduced value. * * If `initialReduction` is not provided, the first item in the * Collection will be used. * * @see `Array#reduce`. */ reduce<R>( reducer: (reduction: R, value: V, key: K, iter?: this) => R, initialReduction: R ): R; reduce<R>(reducer: (reduction: V | R, value: V, key: K, iter?: this) => R): R; /** * True if `predicate` returns true for all entries in the Collection. */ every(predicate: (value: V, key: K, iter?: this) => boolean): boolean; /** * True if `predicate` returns true for any entry in the Collection. */ some(predicate: (value: V, key: K, iter?: this) => boolean): boolean; /** * Returns the size of this Collection by iterating through it. * * If a `predicate` is provided, returns the count of entries in the * Sequence for which the `predicate` returns true. * * NOTE: For concrete collections, this returns size when no args * are passed. For sequences it must always iterate over the whole * collection. */ count(): number; count(predicate: (value: V, key: K, iter?: this) => boolean): number; // Search for value /** * Returns the first value for which the `predicate` returns true. */ find( predicate: (value: V, key: K, iter?: this) => boolean, notSetValue?: V ): V | undefined; // Conversions /** * Converts this collection into the type of collection specified as the * `CollectionConstructor` parameter. If no conversion is neccessary, returns * the original instance. The easiest way to describe the function is to show * some example usages: * * ```js * import {List, Map, Set, IndexedSequence, KeyedSequence, SetSequence, NativeMap, NativeSet} from 'sequins'; * const list = new List([1, 2, 3]); * list.to(Map) // Map{0 => 1, 1 => 2, 2 => 3} * list.to(Set) // Set{1, 2, 3} * list.to(List) // list * list.to(IndexedSequence) // new IndexedSequence(list) * list.to(KeyedSequence) // new KeyedSequence(list) * list.to(SetSequence) // new SetSequence(list) * list.to(NativeMap) // NativeMap{0 => 1, 1 => 2, 2 => 3} * list.to(NativeMap) instanceof global.Map // true * list.to(NativeSet) // NativeSet{1, 2, 3} * list.to(NativeSet) instanceof glo