diff --git a/src/Sequence.ts b/src/Sequence.ts index 351e437..89fde22 100644 --- a/src/Sequence.ts +++ b/src/Sequence.ts @@ -9,6 +9,8 @@ import { NumberKeyedObject, StringKeyedObject, ComparePredicate, + Sequence, + OrderedSequence, } from "./types"; import { concat } from "./transforms/concat"; import { distinct } from "./transforms/distinct"; @@ -43,49 +45,19 @@ const defaultComparer = (a: TKey, b: TKey) => { /** * A sequence of items */ -export class Sequence implements Iterable { +export class SequenceImpl implements Iterable, Sequence { constructor(protected _iterable: Iterable) {} [Symbol.iterator] = (): Iterator => this._iterable[Symbol.iterator](); - /** - * Returns a new sequence that contains the items in the current sequence - * and items from the given iterable. - * - * @example - * ```typescript - * // Returns sequence with values 1, 2, 3, 4 - * from([1, 2]).concat([3, 4]); - * ``` - */ concat(...others: Iterable[]): Sequence { return this._sequenceFromGenerator(concat, others); } - /** - * Returns unique values in the sequence. Uniqueness is checked using - * the '===' operator. - * - * @example - * ```typescript - * // Returns a sequence with the values 4, 5 - * from([4, 4, 5, 4]).distinct(); - * ``` - */ distinct(): Sequence { return this._sequenceFromGenerator(distinct); } - /** - * Checks that all items in the sequence pass the test implemented by the - * provided function. - * - * @example - * ```typescript - * // Returns false - * from([-1, 4, 5, 6]).every(x => x >= 0); - * ``` - */ every(predicate: PredicateFn = identityPredicateFn): boolean { for (const item of this._iterable) { if (!predicate(item)) { @@ -96,30 +68,10 @@ export class Sequence implements Iterable { return true; } - /** - * Returns a new sequence where items are filtered out for which the - * predicate function returns a falsy value. - * - * @example - * ```typescript - * // Returns a squence with the value -1 - * from([-1, 4, 5, 6]).filter(x => x < 0); - * ``` - */ filter(predicate: PredicateFn): Sequence { return this._sequenceFromGenerator(filter, [predicate]); } - /** - * Returns the value of the first element in the sequence that satisfies the - * provided testing function. Otherwise undefined is returned. - * - * @example - * ```typescript - * // Returns 4 - * from([2, 4, 6]).find(x => x === 4); - * ``` - */ find(predicate: PredicateFn): TItem | undefined { for (const item of this._iterable) { if (predicate(item)) { @@ -130,16 +82,6 @@ export class Sequence implements Iterable { return undefined; } - /** - * Returns the first element of the sequence or undefined if - * the sequence is empty. - * - * @example - * ```typescript - * // Returns 1 - * from([1, 3, 5]).first(); - * ``` - */ first(): TItem | undefined { for (const item of this._iterable) { return item; @@ -148,161 +90,28 @@ export class Sequence implements Iterable { return undefined; } - /** - * First maps each element of the sequence using the given mapping function, - * then flattens the result into a new sequence. - * - * @example - * ```typescript - * // Returns [1, 2, 3, 4, 5, 6] - * from([1, 3, 5]).flatMap(x => [x, x + 1]).toArray(); - * ``` - */ flatMap( mapperFn: MapFn ): Sequence { return this._sequenceFromGenerator(flatMap, [mapperFn]); } - /** - * Calls the given callback function with each item in the sequence. - * - * @example - * ```typescript - * // Logs 1, 2 and 3 to console - * from([1, 2, 3]).forEach(i => console.log(i)); - * ``` - */ forEach(callback: CallbackFn): void { for (const item of this._iterable) { callback(item); } } - /** - * Groups the items in the sequence using the given item's key - * - * @param key Key to be used for the grouping - * - * @example - * ```typescript - * from([ - * { name: "John", gender: "M" }, - * { name: "Mike", gender: "M" }, - * { name: "Lisa", gender: "F" }, - * { name: "Mary", gender: "F" } - * ]).groupBy("gender"); - * // Returns a sequence with two groupings: - * // { - * // key: "M", - * // items: [ - * // { name: "John", gender: "M" }, - * // { name: "Mike", gender: "M" } - * // ] - * // }, - * // { - * // key: "F", - * // items: [ - * // { name: "Lisa", gender: "F" }, - * // { name: "Mary", gender: "F" } - * // ] - * // } - * ``` - */ groupBy( key: TKey ): Sequence>; - /** - * Groups the items in the sequence by keys returned by the given - * keySelector function. - * - * @param keySelector A function to extract the key for each element. - * - * @example - * ```typescript - * from([ - * { name: "John", gender: "M" }, - * { name: "Mike", gender: "M" }, - * { name: "Lisa", gender: "F" }, - * { name: "Mary", gender: "F" } - * ]).groupBy(user => user.gender); - * // Returns a sequence with two groupings: - * // { - * // key: "M", - * // items: [ - * // { name: "John", gender: "M" }, - * // { name: "Mike", gender: "M" } - * // ] - * // }, - * // { - * // key: "F", - * // items: [ - * // { name: "Lisa", gender: "F" }, - * // { name: "Mary", gender: "F" } - * // ] - * // } - * ``` - */ groupBy( keySelector: KeySelectorFn ): Sequence>; - /** - * Groups the items of a sequence according to a specified key and - * projects the elements for each group by using a specified function. - * - * @param key Key to be used for the grouping - * @param elementSelector A function to map each source element to an element in an Grouping. - * - * @example - * ```typescript - * from([ - * { name: "John", gender: "M" }, - * { name: "Mike", gender: "M" }, - * { name: "Lisa", gender: "F" }, - * { name: "Mary", gender: "F" } - * ]).groupBy("gender", user => user.name); - * // Returns a sequence with two groupings: - * // { - * // key: "M", - * // items: ["John", "Mike"] - * // }, - * // { - * // key: "F", - * // items: ["Lisa", "Mary"] - * // } - * ``` - */ groupBy( key: TKey, elementSelector: MapFn ): Sequence>; - /** - * Groups the elements of a sequence according to a specified key selector - * function and projects the elements for each group by using a specified - * function. - * - * @param keySelector A function to extract the key for each element. - * @param elementSelector A function to map each source element to an element in an Grouping. - * - * @example - * ```typescript - * from([ - * { name: "John", gender: "M" }, - * { name: "Mike", gender: "M" }, - * { name: "Lisa", gender: "F" }, - * { name: "Mary", gender: "F" } - * ]).groupBy("gender", user => user.name); - * // Returns a sequence with two groupings: - * // { - * // key: "M", - * // items: ["John", "Mike"] - * // }, - * // { - * // key: "F", - * // items: ["Lisa", "Mary"] - * // } - * ``` - */ groupBy( keySelector: KeySelectorFn, elementSelector: MapFn @@ -321,17 +130,6 @@ export class Sequence implements Iterable { ]); } - /** - * Determines whether the sequence includes the given element, - * returning true or false as appropriate. The check is done - * using '==='. - * - * @example - * ```typescript - * // Returns true - * from([1, 2, 3]).includes(3); - * ``` - */ includes(searchItem: TItem): boolean { for (const item of this._iterable) { if (item === searchItem) { @@ -342,66 +140,20 @@ export class Sequence implements Iterable { return false; } - /** - * Returns true if the sequence is empty, false otherwise. - * - * @example - * ```typescript - * // Returns true - * from([]).isEmpty(); - * ``` - */ isEmpty(): boolean { return !this.some(() => true); } - /** - * Returns the first element of the sequence or undefined if - * the sequence is empty. - * - * @example - * ```typescript - * // Returns 5 - * from([1, 3, 5]).last(); - * ``` - */ last(): TItem | undefined { const items = this.toArray(); return items.length === 0 ? undefined : items[items.length - 1]; } - /** - * Maps the sequence to a new sequence where each item is converted - * to a new value using the given mapper function. - * - * @example - * ```typescript - * // Returns [2, 4, 6] - * from([1, 2, 3]).map(x => x * 2); - * ``` - */ map(mapFn: MapFn): Sequence { return this._sequenceFromGenerator(map, [mapFn]); } - /** - * Maps each item in the sequence to an object composed of the picked - * object properties. - * - * @example - * ```typescript - * const users = [ - * { id: 1, name: "John", age: 31, active: true }, - * { id: 2, name: "Jane", age: 32, active: false }, - * { id: 3, name: "Luke", age: 33, active: false }, - * { id: 4, name: "Mary", age: 34, active: true }, - * ]; - * - * // Returns a Sequence of { name: 'John' }, { name: 'Jane' }, { name: 'Luke' }, { name: 'Mary' } - * from(users).pick("name"); - * ``` - */ pick( ...keys: TKeys[] ): Sequence<{ [P in TKeys]: TItem[P] }> { @@ -418,34 +170,10 @@ export class Sequence implements Iterable { }); } - /** - * This method yields the elements from the provided items first, followed by the items in the - * underlying sequence. - * - * @param items The provided set of items that should be in the prepended to the Sequence. - * - * @example - * ```ts - * // returns [4, 5, 6, 1, 2, 3] - * from([1, 2, 3]) - * .prepend([4, 5, 6]) - * .toArray(); - * ``` - */ prepend(...items: Iterable[]): Sequence { return this._sequenceFromGenerator(prepend, items); } - /** - * Executes a reducer function on each item in the sequence resulting - * in a single output value. - * - * @example - * ```typescript - * // Returns a 15 - * from([1, 2, 3, 4, 5]).reduce((x, acc) => acc+x, 0) - * ``` - */ reduce( callback: ReduceCallbackFn, accumulator: TResult @@ -457,60 +185,18 @@ export class Sequence implements Iterable { return accumulator; } - /** - * Reverses the order of the items in the sequence - * - * @example - * ```typescript - * // Returns [3, 2, 1] - * from([1, 2, 3]).reverse().toArray(); - * ``` - */ reverse(): Sequence { return this._sequenceFromGenerator(reverse); } - /** - * Skips the first N items in the sequence - * - * @example - * ```typescript - * // Returns [3, 4] - * from([1, 2, 3, 4]).skip(2); - * ``` - */ skip(howMany: number): Sequence { return this._sequenceFromGenerator(skip, [howMany]); } - /** - * Bypasses elements in a sequence as long as a specified condition is true - * and then returns the remaining elements. - * - * @param predicate A function to test each element for a condition. - * - * @example - * ```ts - * // Returns [3, 4, 5] - * from([1, 2, 3, 4, 5]) - * .skipWhile(i => i < 3) - * .toArray(); - * ``` - */ skipWhile(predicate: PredicateFn): Sequence { return this._sequenceFromGenerator(skipWhile, [predicate]); } - /** - * Returns true if sequence contains an element for which the given - * predicate returns a truthy value. - * - * @example - * ```typescript - * // Returns true - * from([1, 2, 3]).some(x => x === 1) - * ``` - */ some(predicate: PredicateFn = identityPredicateFn): boolean { for (const item of this._iterable) { if (predicate(item)) { @@ -520,142 +206,32 @@ export class Sequence implements Iterable { return false; } - /** - * Sorts the elements of a sequence in ascending order according to a key - * by using a specified comparer. - * - * @param keySelector A function to extract a key from an element. - * @param comparer A function to compare the keys - */ - /** - * @example - * ```typescript - * // Returns a Sequence of 1, 2, 3 - * from([1, 3, 2]).sortBy() - * ``` - */ sortBy(): OrderedSequence; - /** - * @example - * ```typescript - * const users = [ - * { id: 2, name: "Jane" }, - * { id: 4, name: "Mary" }, - * { id: 1, name: "John" }, - * { id: 3, name: "Luke" }, - * ]; - * - * // Returns a Sequence of - * // { id: 1, name: 'John' }, - * // { id: 2, name: 'Jane' }, - * // { id: 3, name: 'Luke' }, - * // { id: 4, name: 'Mary' } - * from(users).sortBy(user => user.id) - * ``` - */ sortBy( keySelector: KeySelectorFn, comparer?: ComparerFn ): OrderedSequence; - /** - * @example - * ```typescript - * const users = [ - * { id: 2, name: "Jane" }, - * { id: 4, name: "Mary" }, - * { id: 1, name: "John" }, - * { id: 3, name: "Luke" }, - * ]; - * - * // Returns a Sequence of - * // Returns a Sequence of - * // { id: 4, name: 'Mary' }, - * // { id: 3, name: 'Luke' }, - * // { id: 2, name: 'Jane' }, - * // { id: 1, name: 'John' } - * from(users).sortBy(user => user.id) - * ``` - */ sortBy( keySelector?: KeySelectorFn, comparer?: ComparerFn ): OrderedSequence { const compareFn = createCompareFn(false, keySelector, comparer); - return new OrderedSequence(this._iterable, compareFn); - } - - /** - * Sorts the elements of a sequence in descending order according to a key - * by using a specified comparer. - * - * @param keySelector A function to extract a key from an element. - * @param comparer A function to compare the keys - * - * @example - * ```typescript - * // Returns a Sequence of 3, 2, 1 - * from([1, 3, 2]).sortByDescending() - * ``` - */ + return new OrderedSequenceImpl(this._iterable, compareFn); + } + sortByDescending( keySelector?: KeySelectorFn, comparer?: ComparerFn ): OrderedSequence { const compareFn = createCompareFn(true, keySelector, comparer); - return new OrderedSequence(this._iterable, compareFn); + return new OrderedSequenceImpl(this._iterable, compareFn); } - /** - * Sums the elements in the sequence. - * NOTE! If the sequence is empty, 0 is returned. - * - * @example - * ```typescript - * // Returns 6 - * from([1, 2, 3]).sum(); - * ``` - */ sum(): number; - /** - * Sums the elements in the sequence - * NOTE! If the sequence is empty, 0 is returned. - * - * @example - * ```typescript - * // Returns "abc" - * from(["a", "b", "c"]).sum(); - * ``` - */ sum(): string; - /** - * Maps the elements in the sequence using the valueSelector and sums them - * together. - * NOTE! If the sequence is empty, 0 is returned. - * - * @param valueSelector A function to select a value from an element. - * - * @example - * ```typescript - * // Returns 2 - * from([true, false, true]).sum(x => x ? 1 : 0); - * ``` - */ sum(valueSelector: MapFn): number; - /** - * Maps the elements in the sequence using the valueSelector and sums them - * together. - * NOTE! If the sequence is empty, 0 is returned. - * - * @param valueSelector A function to select a value from an element. - * - * @example - * ```typescript - * // Returns "101" - * from([true, false, true]).sum(x => x ? "1" : "0"); - * ``` - */ sum(valueSelector: MapFn): string; sum(valueSelector?: MapFn): number | string { let result: any | undefined = undefined; @@ -676,56 +252,14 @@ export class Sequence implements Iterable { return result === undefined ? 0 : result; } - /** - * Takes the firt N items from the sequence - * - * @example - * ```typescript - * // Returns [1, 2] - * from([1, 2, 3, 4]).take(2); - * ``` - */ take(howMany: number): Sequence { return this._sequenceFromGenerator(take, [howMany]); } - /** - * Returns elements from a sequence as long as a specified condition is true, - * and then skips the remaining elements. - * - * @param predicate A function to test each element for a condition. - * - * @example - * ```ts - * // Returns [1, 2] - * from([1, 2, 3, 4, 5]) - * .takeWhile(i => i < 3) - * .toArray(); - * ``` - */ takeWhile(predicate: PredicateFn) { return this._sequenceFromGenerator(takeWhile, [predicate]); } - /** - * Returns elements from a sequence as long as they don't exist in the specified iterable items. - * - * @param items The provided set of items that should not be in the returned Sequence. - * @param predicate The optional predicate that determines if two TItem items are equal. - * - * @example - * ```ts - * // returns [2, 4, 6] - * from([1, 2, 3, 4, 5, 6]) - * .without([1, 3, 5]) - * .toArray(); - * - * // returns [{ id: 1 }, { id: 3 }] - * from([{ id: 1 }, { id: 2 }, { id: 3 }]) - * .without([{ id: 2 }], (a, b) => a.id === b.id) - * .toArray(); - * ``` - */ without( items: Iterable, predicate?: ComparePredicate @@ -738,35 +272,10 @@ export class Sequence implements Iterable { } } - /** - * Converts the sequence to an array - * - * @example - * ```typescript - * // Return [1, 2, 3] - * from([1, 2, 3]).toArray(); - * ``` - */ toArray(): TItem[] { return copyIntoAnArray(this._iterable); } - /** - * Converts the sequence to a Map using the given keySelectorFn and - * possible elementSelectorFn. - * - * @example - * ```typescript - * // Returns map with elements: - * // 1 -> { id: 1, name: "John" } - * // 2 -> { id: 2, name: "Jane"} - * const users = [{ id: 1, name: "John" }, { id: 2, name: "Jane"}] - * from(users).toMap(u => u.id); - * ``` - * - * @param keySelectorFn - * @param elementSelectorFn - */ toMap( keySelectorFn: MapFn, elementSelectorFn?: MapFn @@ -783,24 +292,6 @@ export class Sequence implements Iterable { return map; } - /** - * Converts the sequence to an object using the given keySelectorFn and - * possible elementSelectorFn. - * - * @example - * ```typescript - * // Returns an object: - * // { - * // "John": { id: 1, name: "John" }, - * // "Jane": { id: 2, name: "Jane"} - * // } - * const users = [{ id: 1, name: "John" }, { id: 2, name: "Jane"}] - * from(users).toObject(u => u.name); - * ``` - * - * @param keySelectorFn - * @param elementSelectorFn - */ toObject(keySelectorFn: MapFn): StringKeyedObject; toObject(keySelectorFn: MapFn): NumberKeyedObject; toObject( @@ -831,15 +322,6 @@ export class Sequence implements Iterable { return object; } - /** - * Converts the sequence to a Set - * - * @example - * ```typescript - * // Return a Set with elements 1, 2, 3 - * from([1, 1, 2, 3]).toSet(); - * ``` - */ toSet(): Set { return new Set(this._iterable); } @@ -850,7 +332,7 @@ export class Sequence implements Iterable { ) { const iterableArg = [this._iterable]; - return new Sequence( + return new SequenceImpl( iterableFromGenerator( factoryFn, restArgs ? iterableArg.concat(restArgs) : iterableArg @@ -862,7 +344,8 @@ export class Sequence implements Iterable { /** * Ordered sequence of elements */ -export class OrderedSequence extends Sequence { +export class OrderedSequenceImpl extends SequenceImpl + implements OrderedSequence { private _comparer: ComparerFn; constructor( @@ -881,7 +364,7 @@ export class OrderedSequence extends Sequence { const thenComparer = createCompareFn(false, keySelector, comparer); const compareFn = createChainedCompareFn(this._comparer, thenComparer); - return new OrderedSequence(this._iterableToSort, compareFn); + return new OrderedSequenceImpl(this._iterableToSort, compareFn); } thenByDescending( @@ -891,7 +374,7 @@ export class OrderedSequence extends Sequence { const thenComparer = createCompareFn(true, keySelector, comparer); const compareFn = createChainedCompareFn(this._comparer, thenComparer); - return new OrderedSequence(this._iterableToSort, compareFn); + return new OrderedSequenceImpl(this._iterableToSort, compareFn); } } diff --git a/src/fromfrom.ts b/src/fromfrom.ts index f8638a2..1a04f4c 100644 --- a/src/fromfrom.ts +++ b/src/fromfrom.ts @@ -1,9 +1,26 @@ /** * Entrypoint of the library */ -import { Sequence } from "./Sequence"; +import { SequenceImpl } from "./Sequence"; import { createObjectIterable } from "./ObjectIterable"; -import { StringKeyedObject, NumberKeyedObject } from "./types"; +import { + CallbackFn, + ComparePredicate, + ComparerFn, + Grouping, + KeySelectorFn, + MapFn, + NumberKeyedObject, + OrderedSequence, + PredicateFn, + ReduceCallbackFn, + Sequence, + StringKeyedObject, +} from "./types"; + +function _isIterable(object: any): object is Iterable { + return typeof object[Symbol.iterator] === "function"; +} export function from(iterable: Iterable): Sequence; export function from(object: StringKeyedObject): Sequence<[string, T]>; @@ -11,15 +28,26 @@ export function from(object: NumberKeyedObject): Sequence<[number, T]>; export function from( iterable: Iterable | StringKeyedObject | NumberKeyedObject ): Sequence | Sequence<[string, T]> | Sequence<[number, T]> { - if (iterable instanceof Sequence) { + if (iterable instanceof SequenceImpl) { return iterable; } else if (_isIterable(iterable)) { - return new Sequence(iterable); + return new SequenceImpl(iterable); } else { - return new Sequence(createObjectIterable(iterable)); + return new SequenceImpl(createObjectIterable(iterable)); } } -function _isIterable(object: any): object is Iterable { - return typeof object[Symbol.iterator] === "function"; -} +export { + CallbackFn, + ComparePredicate, + ComparerFn, + Grouping, + KeySelectorFn, + MapFn, + NumberKeyedObject, + OrderedSequence, + PredicateFn, + ReduceCallbackFn, + Sequence, + StringKeyedObject, +}; diff --git a/src/types.ts b/src/types.ts index f10272e..e7079e2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -29,3 +29,599 @@ export interface StringKeyedObject { export interface NumberKeyedObject { [index: number]: T; } + +/** + * A sequence of items + */ +export interface Sequence extends Iterable { + /** + * Returns a new sequence that contains the items in the current sequence + * and items from the given iterable. + * + * @example + * ```typescript + * // Returns sequence with values 1, 2, 3, 4 + * from([1, 2]).concat([3, 4]); + * ``` + */ + concat(...others: Iterable[]): Sequence; + /** + * Returns unique values in the sequence. Uniqueness is checked using + * the '===' operator. + * + * @example + * ```typescript + * // Returns a sequence with the values 4, 5 + * from([4, 4, 5, 4]).distinct(); + * ``` + */ + distinct(): Sequence; + /** + * Checks that all items in the sequence pass the test implemented by the + * provided function. + * + * @example + * ```typescript + * // Returns false + * from([-1, 4, 5, 6]).every(x => x >= 0); + * ``` + */ + every(predicate?: PredicateFn): boolean; + /** + * Returns a new sequence where items are filtered out for which the + * predicate function returns a falsy value. + * + * @example + * ```typescript + * // Returns a squence with the value -1 + * from([-1, 4, 5, 6]).filter(x => x < 0); + * ``` + */ + filter(predicate: PredicateFn): Sequence; + /** + * Returns the value of the first element in the sequence that satisfies the + * provided testing function. Otherwise undefined is returned. + * + * @example + * ```typescript + * // Returns 4 + * from([2, 4, 6]).find(x => x === 4); + * ``` + */ + find(predicate: PredicateFn): TItem | undefined; + /** + * Returns the first element of the sequence or undefined if + * the sequence is empty. + * + * @example + * ```typescript + * // Returns 1 + * from([1, 3, 5]).first(); + * ``` + */ + first(): TItem | undefined; + /** + * First maps each element of the sequence using the given mapping function, + * then flattens the result into a new sequence. + * + * @example + * ```typescript + * // Returns [1, 2, 3, 4, 5, 6] + * from([1, 3, 5]).flatMap(x => [x, x + 1]).toArray(); + * ``` + */ + flatMap( + mapperFn: MapFn + ): Sequence; + /** + * Calls the given callback function with each item in the sequence. + * + * @example + * ```typescript + * // Logs 1, 2 and 3 to console + * from([1, 2, 3]).forEach(i => console.log(i)); + * ``` + */ + forEach(callback: CallbackFn): void; + /** + * Groups the items in the sequence using the given item's key + * + * @param key Key to be used for the grouping + * + * @example + * ```typescript + * from([ + * { name: "John", gender: "M" }, + * { name: "Mike", gender: "M" }, + * { name: "Lisa", gender: "F" }, + * { name: "Mary", gender: "F" } + * ]).groupBy("gender"); + * // Returns a sequence with two groupings: + * // { + * // key: "M", + * // items: [ + * // { name: "John", gender: "M" }, + * // { name: "Mike", gender: "M" } + * // ] + * // }, + * // { + * // key: "F", + * // items: [ + * // { name: "Lisa", gender: "F" }, + * // { name: "Mary", gender: "F" } + * // ] + * // } + * ``` + */ + groupBy( + key: TKey + ): Sequence>; + /** + * Groups the items in the sequence by keys returned by the given + * keySelector function. + * + * @param keySelector A function to extract the key for each element. + * + * @example + * ```typescript + * from([ + * { name: "John", gender: "M" }, + * { name: "Mike", gender: "M" }, + * { name: "Lisa", gender: "F" }, + * { name: "Mary", gender: "F" } + * ]).groupBy(user => user.gender); + * // Returns a sequence with two groupings: + * // { + * // key: "M", + * // items: [ + * // { name: "John", gender: "M" }, + * // { name: "Mike", gender: "M" } + * // ] + * // }, + * // { + * // key: "F", + * // items: [ + * // { name: "Lisa", gender: "F" }, + * // { name: "Mary", gender: "F" } + * // ] + * // } + * ``` + */ + groupBy( + keySelector: KeySelectorFn + ): Sequence>; + /** + * Groups the items of a sequence according to a specified key and + * projects the elements for each group by using a specified function. + * + * @param key Key to be used for the grouping + * @param elementSelector A function to map each source element to an element in an Grouping. + * + * @example + * ```typescript + * from([ + * { name: "John", gender: "M" }, + * { name: "Mike", gender: "M" }, + * { name: "Lisa", gender: "F" }, + * { name: "Mary", gender: "F" } + * ]).groupBy("gender", user => user.name); + * // Returns a sequence with two groupings: + * // { + * // key: "M", + * // items: ["John", "Mike"] + * // }, + * // { + * // key: "F", + * // items: ["Lisa", "Mary"] + * // } + * ``` + */ + groupBy( + key: TKey, + elementSelector: MapFn + ): Sequence>; + /** + * Groups the elements of a sequence according to a specified key selector + * function and projects the elements for each group by using a specified + * function. + * + * @param keySelector A function to extract the key for each element. + * @param elementSelector A function to map each source element to an element in an Grouping. + * + * @example + * ```typescript + * from([ + * { name: "John", gender: "M" }, + * { name: "Mike", gender: "M" }, + * { name: "Lisa", gender: "F" }, + * { name: "Mary", gender: "F" } + * ]).groupBy("gender", user => user.name); + * // Returns a sequence with two groupings: + * // { + * // key: "M", + * // items: ["John", "Mike"] + * // }, + * // { + * // key: "F", + * // items: ["Lisa", "Mary"] + * // } + * ``` + */ + groupBy( + keySelector: KeySelectorFn, + elementSelector: MapFn + ): Sequence>; + /** + * Determines whether the sequence includes the given element, + * returning true or false as appropriate. The check is done + * using '==='. + * + * @example + * ```typescript + * // Returns true + * from([1, 2, 3]).includes(3); + * ``` + */ + includes(searchItem: TItem): boolean; + /** + * Returns true if the sequence is empty, false otherwise. + * + * @example + * ```typescript + * // Returns true + * from([]).isEmpty(); + * ``` + */ + isEmpty(): boolean; + /** + * Returns the first element of the sequence or undefined if + * the sequence is empty. + * + * @example + * ```typescript + * // Returns 5 + * from([1, 3, 5]).last(); + * ``` + */ + last(): TItem | undefined; + /** + * Maps the sequence to a new sequence where each item is converted + * to a new value using the given mapper function. + * + * @example + * ```typescript + * // Returns [2, 4, 6] + * from([1, 2, 3]).map(x => x * 2); + * ``` + */ + map(mapFn: MapFn): Sequence; + /** + * Maps each item in the sequence to an object composed of the picked + * object properties. + * + * @example + * ```typescript + * const users = [ + * { id: 1, name: "John", age: 31, active: true }, + * { id: 2, name: "Jane", age: 32, active: false }, + * { id: 3, name: "Luke", age: 33, active: false }, + * { id: 4, name: "Mary", age: 34, active: true }, + * ]; + * + * // Returns a Sequence of { name: 'John' }, { name: 'Jane' }, { name: 'Luke' }, { name: 'Mary' } + * from(users).pick("name"); + * ``` + */ + pick( + ...keys: TKeys[] + ): Sequence<{ [P in TKeys]: TItem[P] }>; + /** + * This method yields the elements from the provided items first, followed by the items in the + * underlying sequence. + * + * @param items The provided set of items that should be in the prepended to the Sequence. + * + * @example + * ```ts + * // returns [4, 5, 6, 1, 2, 3] + * from([1, 2, 3]) + * .prepend([4, 5, 6]) + * .toArray(); + * ``` + */ + prepend(...items: Iterable[]): Sequence; + /** + * Executes a reducer function on each item in the sequence resulting + * in a single output value. + * + * @example + * ```typescript + * // Returns a 15 + * from([1, 2, 3, 4, 5]).reduce((x, acc) => acc+x, 0) + * ``` + */ + reduce( + callback: ReduceCallbackFn, + accumulator: TResult + ): TResult; + /** + * Reverses the order of the items in the sequence + * + * @example + * ```typescript + * // Returns [3, 2, 1] + * from([1, 2, 3]).reverse().toArray(); + * ``` + */ + reverse(): Sequence; + /** + * Skips the first N items in the sequence + * + * @example + * ```typescript + * // Returns [3, 4] + * from([1, 2, 3, 4]).skip(2); + * ``` + */ + skip(howMany: number): Sequence; + /** + * Bypasses elements in a sequence as long as a specified condition is true + * and then returns the remaining elements. + * + * @param predicate A function to test each element for a condition. + * + * @example + * ```ts + * // Returns [3, 4, 5] + * from([1, 2, 3, 4, 5]) + * .skipWhile(i => i < 3) + * .toArray(); + * ``` + */ + skipWhile(predicate: PredicateFn): Sequence; + /** + * Returns true if sequence contains an element for which the given + * predicate returns a truthy value. + * + * @example + * ```typescript + * // Returns true + * from([1, 2, 3]).some(x => x === 1) + * ``` + */ + some(predicate?: PredicateFn): boolean; + /** + * Sorts the elements of a sequence in ascending order according to a key + * by using a specified comparer. + * + * @param keySelector A function to extract a key from an element. + * @param comparer A function to compare the keys + */ + /** + * @example + * ```typescript + * // Returns a Sequence of 1, 2, 3 + * from([1, 3, 2]).sortBy() + * ``` + */ + sortBy(): OrderedSequence; + /** + * @example + * ```typescript + * const users = [ + * { id: 2, name: "Jane" }, + * { id: 4, name: "Mary" }, + * { id: 1, name: "John" }, + * { id: 3, name: "Luke" }, + * ]; + * + * // Returns a Sequence of + * // { id: 1, name: 'John' }, + * // { id: 2, name: 'Jane' }, + * // { id: 3, name: 'Luke' }, + * // { id: 4, name: 'Mary' } + * from(users).sortBy(user => user.id) + * ``` + */ + sortBy( + keySelector: KeySelectorFn, + comparer?: ComparerFn + ): OrderedSequence; + /** + * Sorts the elements of a sequence in descending order according to a key + * by using a specified comparer. + * + * @param keySelector A function to extract a key from an element. + * @param comparer A function to compare the keys + * + * @example + * ```typescript + * // Returns a Sequence of 3, 2, 1 + * from([1, 3, 2]).sortByDescending() + * ``` + */ + sortByDescending( + keySelector?: KeySelectorFn, + comparer?: ComparerFn + ): OrderedSequence; + /** + * Sums the elements in the sequence. + * NOTE! If the sequence is empty, 0 is returned. + * + * @example + * ```typescript + * // Returns 6 + * from([1, 2, 3]).sum(); + * ``` + */ + sum(): number; + /** + * Sums the elements in the sequence + * NOTE! If the sequence is empty, 0 is returned. + * + * @example + * ```typescript + * // Returns "abc" + * from(["a", "b", "c"]).sum(); + * ``` + */ + sum(): string; + /** + * Maps the elements in the sequence using the valueSelector and sums them + * together. + * NOTE! If the sequence is empty, 0 is returned. + * + * @param valueSelector A function to select a value from an element. + * + * @example + * ```typescript + * // Returns 2 + * from([true, false, true]).sum(x => x ? 1 : 0); + * ``` + */ + sum(valueSelector: MapFn): number; + /** + * Maps the elements in the sequence using the valueSelector and sums them + * together. + * NOTE! If the sequence is empty, 0 is returned. + * + * @param valueSelector A function to select a value from an element. + * + * @example + * ```typescript + * // Returns "101" + * from([true, false, true]).sum(x => x ? "1" : "0"); + * ``` + */ + sum(valueSelector: MapFn): string; + /** + * Takes the firt N items from the sequence + * + * @example + * ```typescript + * // Returns [1, 2] + * from([1, 2, 3, 4]).take(2); + * ``` + */ + take(howMany: number): Sequence; + /** + * Returns elements from a sequence as long as a specified condition is true, + * and then skips the remaining elements. + * + * @param predicate A function to test each element for a condition. + * + * @example + * ```ts + * // Returns [1, 2] + * from([1, 2, 3, 4, 5]) + * .takeWhile(i => i < 3) + * .toArray(); + * ``` + */ + takeWhile(predicate: PredicateFn): Sequence; + /** + * Returns elements from a sequence as long as they don't exist in the specified iterable items. + * + * @param items The provided set of items that should not be in the returned Sequence. + * @param predicate The optional predicate that determines if two TItem items are equal. + * + * @example + * ```ts + * // returns [2, 4, 6] + * from([1, 2, 3, 4, 5, 6]) + * .without([1, 3, 5]) + * .toArray(); + * + * // returns [{ id: 1 }, { id: 3 }] + * from([{ id: 1 }, { id: 2 }, { id: 3 }]) + * .without([{ id: 2 }], (a, b) => a.id === b.id) + * .toArray(); + * ``` + */ + without( + items: Iterable, + predicate?: ComparePredicate + ): Sequence; + /** + * Converts the sequence to an array + * + * @example + * ```typescript + * // Return [1, 2, 3] + * from([1, 2, 3]).toArray(); + * ``` + */ + toArray(): TItem[]; + /** + * Converts the sequence to a Map using the given keySelectorFn and + * possible elementSelectorFn. + * + * @example + * ```typescript + * // Returns map with elements: + * // 1 -> { id: 1, name: "John" } + * // 2 -> { id: 2, name: "Jane"} + * const users = [{ id: 1, name: "John" }, { id: 2, name: "Jane"}] + * from(users).toMap(u => u.id); + * ``` + * + * @param keySelectorFn + * @param elementSelectorFn + */ + toMap( + keySelectorFn: MapFn, + elementSelectorFn?: MapFn + ): Map; + /** + * Converts the sequence to an object using the given keySelectorFn and + * possible elementSelectorFn. + * + * @example + * ```typescript + * // Returns an object: + * // { + * // "John": { id: 1, name: "John" }, + * // "Jane": { id: 2, name: "Jane"} + * // } + * const users = [{ id: 1, name: "John" }, { id: 2, name: "Jane"}] + * from(users).toObject(u => u.name); + * ``` + * + * @param keySelectorFn + * @param elementSelectorFn + */ + toObject(keySelectorFn: MapFn): StringKeyedObject; + toObject(keySelectorFn: MapFn): NumberKeyedObject; + toObject( + keySelectorFn: MapFn, + elementSelectorFn: MapFn + ): StringKeyedObject; + toObject( + keySelectorFn: MapFn, + elementSelectorFn: MapFn + ): NumberKeyedObject; + /** + * Converts the sequence to a Set + * + * @example + * ```typescript + * // Return a Set with elements 1, 2, 3 + * from([1, 1, 2, 3]).toSet(); + * ``` + */ + toSet(): Set; +} + +/** + * Ordered sequence of elements + */ +export interface OrderedSequence extends Sequence { + thenBy( + keySelector: KeySelectorFn, + comparer?: ComparerFn + ): OrderedSequence; + thenByDescending( + keySelector: KeySelectorFn, + comparer?: ComparerFn + ): OrderedSequence; +}