diff --git a/package.json b/package.json index 4a879b4..7ce515b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "coverage": "rimraf coverage && jest --coverage", "travis": "yarn run lint && yarn test", "lint": "node_modules/.bin/tslint -c tslint.json src/**/*.ts test/**/*.ts", - "docs": "rimraf docs && typedoc --name Sequency --readme APIDOC.md --module commonjs --out docs --target es6 --hideGenerator --gaID UA-48569937-1 src", + "docs": "rimraf docs && typedoc --name Sequency --readme APIDOC.md --module commonjs --out docs --excludeNotExported --target es6 --hideGenerator --gaID UA-48569937-1 src", "docs-publish": "yarn run docs && touch docs/.nojekyll && gh-pages -d docs -t", "build": "rimraf lib && rimraf lib-umd && yarn run lint && tsc && yarn test && webpack && size-limit", "build-prod": "yarn run build && yarn run docs-publish", diff --git a/src/Sequence.ts b/src/Sequence.ts index 7241963..283467f 100644 --- a/src/Sequence.ts +++ b/src/Sequence.ts @@ -1,159 +1,112 @@ import SequenceIterator, {GeneratorIterator, GeneratorSeedIterator, IterableIterator} from "./SequenceIterator"; -import map from "./map"; -import filter from "./filter"; -import flatMap from "./flatMap"; -import firstOrNull from "./firstOrNull"; -import first from "./first"; -import lastOrNull from "./lastOrNull"; -import onEach from "./onEach"; -import forEach from "./forEach"; -import toArray from "./toArray"; -import last from "./last"; -import all from "./all"; -import any from "./any"; -import none from "./none"; -import count from "./count"; -import distinct from "./distinct"; -import contains from "./contains"; -import indexOf from "./indexOf"; -import elementAt from "./elementAt"; -import elementAtOrNull from "./elementAtOrNull"; -import elementAtOrElse from "./elementAtOrElse"; -import indexOfFirst from "./indexOfFirst"; -import indexOfLast from "./indexOfLast"; -import joinToString from "./joinToString"; -import mapIndexed from "./mapIndexed"; -import withIndex from "./withIndex"; -import filterIndexed from "./filterIndexed"; -import forEachIndexed from "./forEachIndexed"; -import distinctBy from "./distinctBy"; -import drop from "./drop"; -import take from "./take"; -import single from "./single"; -import singleOrNull from "./singleOrNull"; -import filterNot from "./filterNot"; -import associate from "./associate"; -import associateBy from "./associateBy"; -import groupBy from "./groupBy"; -import reduce from "./reduce"; -import reduceIndexed from "./reduceIndexed"; -import fold from "./fold"; -import foldIndexed from "./foldIndexed"; -import flatten from "./flatten"; -import sorted from "./sorted"; -import sortedBy from "./sortedBy"; -import sortedDescending from "./sortedDescending"; -import sortedByDescending from "./sortedByDescending"; -import sortedWith from "./sortedWith"; -import filterNotNull from "./filterNotNull"; -import mapNotNull from "./mapNotNull"; -import plus from "./plus"; -import minus from "./minus"; -import zip from "./zip"; -import unzip from "./unzip"; -import partition from "./partition"; -import toSet from "./toSet"; -import toMap from "./toMap"; -import sum from "./sum"; -import sumBy from "./sumBy"; -import chunk from "./chunk"; -import reverse from "./reverse"; -import average from "./average"; -import max from "./max"; -import maxBy from "./maxBy"; -import min from "./min"; -import minBy from "./minBy"; -import maxWith from "./maxWith"; -import minWith from "./minWith"; -import dropWhile from "./dropWhile"; -import takeWhile from "./takeWhile"; -import asIterable from "./asIterable"; -import merge from "./merge"; +import {All} from "./all"; +import {Any} from "./any"; +import {AsIterable} from "./asIterable"; +import {Associate} from "./associate"; +import {AssociateBy} from "./associateBy"; +import {Average} from "./average"; +import {Chunk} from "./chunk"; +import {Contains} from "./contains"; +import {Count} from "./count"; +import {Distinct} from "./distinct"; +import {DistinctBy} from "./distinctBy"; +import {Drop} from "./drop"; +import {DropWhile} from "./dropWhile"; +import {ElementAt} from "./elementAt"; +import {ElementAtOrElse} from "./elementAtOrElse"; +import {ElementAtOrNull} from "./elementAtOrNull"; +import {Filter} from "./filter"; +import {FilterIndexed} from "./filterIndexed"; +import {FilterNot} from "./filterNot"; +import {FilterNotNull} from "./filterNotNull"; +import {First} from "./first"; +import {FirstOrNull} from "./firstOrNull"; +import {FlatMap} from "./flatMap"; +import {Flatten} from "./flatten"; +import {Fold} from "./fold"; +import {FoldIndexed} from "./foldIndexed"; +import {ForEach} from "./forEach"; +import {ForEachIndexed} from "./forEachIndexed"; +import {GroupBy} from "./groupBy"; +import {IndexOf} from "./indexOf"; +import {IndexOfFirst} from "./indexOfFirst"; +import {IndexOfLast} from "./indexOfLast"; +import {JoinToString} from "./joinToString"; +import {Last} from "./last"; +import {LastOrNull} from "./lastOrNull"; +import {Map} from "./map"; +import {MapIndexed} from "./mapIndexed"; +import {MapNotNull} from "./mapNotNull"; +import {Max} from "./max"; +import {MaxBy} from "./maxBy"; +import {MaxWith} from "./maxWith"; +import {Merge} from "./merge"; +import {Min} from "./min"; +import {MinBy} from "./minBy"; +import {Minus} from "./minus"; +import {MinWith} from "./minWith"; +import {None} from "./none"; +import {OnEach} from "./onEach"; +import {Partition} from "./partition"; +import {Plus} from "./plus"; +import {Reduce} from "./reduce"; +import {ReduceIndexed} from "./reduceIndexed"; +import {Reverse} from "./reverse"; +import {Single} from "./single"; +import {SingleOrNull} from "./singleOrNull"; +import {Sorted} from "./sorted"; +import {SortedBy} from "./sortedBy"; +import {SortedByDescending} from "./sortedByDescending"; +import {SortedDescending} from "./sortedDescending"; +import {SortedWith} from "./sortedWith"; +import {Sum} from "./sum"; +import {SumBy} from "./sumBy"; +import {Take} from "./take"; +import {TakeWhile} from "./takeWhile"; +import {ToArray} from "./toArray"; +import {ToMap} from "./toMap"; +import {ToSet} from "./toSet"; +import {Unzip} from "./unzip"; +import {WithIndex} from "./withIndex"; +import {Zip} from "./zip"; /** - * A Sequence accepts an iterator and provides a fluent functional API consisting + * @hidden + */ +export interface SequenceOperators extends All, Any, AsIterable, Associate, AssociateBy, Average, Chunk, Contains, Count, Distinct, DistinctBy, Drop, + DropWhile, ElementAt, ElementAtOrElse, ElementAtOrNull, Filter, FilterIndexed, FilterNot, FilterNotNull, First, FirstOrNull, FlatMap, Flatten, Fold, FoldIndexed, + ForEach, ForEachIndexed, GroupBy, IndexOf, IndexOfFirst, IndexOfLast, JoinToString, Last, LastOrNull, Map, MapIndexed, MapNotNull, Max, MaxBy, MaxWith, Merge, Min, MinBy, + Minus, MinWith, None, OnEach, Partition, Plus, Reduce, ReduceIndexed, Reverse, Single, SingleOrNull, Sorted, SortedBy, SortedByDescending, SortedDescending, SortedWith, + Sum, SumBy, Take, TakeWhile, ToArray, ToMap, ToSet, Unzip, WithIndex, Zip { +} + +/** + * A Sequence provides a fluent functional API consisting * of various intermediate and terminal operations for processing the iterated data. * The operations are evaluated lazily to avoid examining all of the input data * when it's not necessary. Sequences can be iterated only once. */ -export default class Sequence { +export default interface Sequence extends SequenceOperators { + readonly iterator: SequenceIterator; +} + +class SequenceImpl { constructor(readonly iterator: SequenceIterator) { } +} + +applyMixins(SequenceImpl, [All, Any, AsIterable, Associate, AssociateBy, Average, Chunk, Contains, Count, Distinct, DistinctBy, Drop, + DropWhile, ElementAt, ElementAtOrElse, ElementAtOrNull, Filter, FilterIndexed, FilterNot, FilterNotNull, First, FirstOrNull, FlatMap, Flatten, Fold, FoldIndexed, + ForEach, ForEachIndexed, GroupBy, IndexOf, IndexOfFirst, IndexOfLast, JoinToString, Last, LastOrNull, Map, MapIndexed, MapNotNull, Max, MaxBy, MaxWith, Merge, Min, MinBy, + Minus, MinWith, None, OnEach, Partition, Plus, Reduce, ReduceIndexed, Reverse, Single, SingleOrNull, Sorted, SortedBy, SortedByDescending, SortedDescending, SortedWith, + Sum, SumBy, Take, TakeWhile, ToArray, ToMap, ToSet, Unzip, WithIndex, Zip]); - map = map; - mapNotNull = mapNotNull; - mapIndexed = mapIndexed; - filter = filter; - filterNot = filterNot; - filterNotNull = filterNotNull; - filterIndexed = filterIndexed; - flatMap = flatMap; - distinct = distinct; - distinctBy = distinctBy; - withIndex = withIndex; - drop = drop; - dropWhile = dropWhile; - take = take; - takeWhile = takeWhile; - onEach = onEach; - flatten = flatten; - sorted = sorted; - sortedDescending = sortedDescending; - sortedBy = sortedBy; - sortedByDescending = sortedByDescending; - sortedWith = sortedWith; - reverse = reverse; - forEach = forEach; - forEachIndexed = forEachIndexed; - toArray = toArray; - toList = toArray; - toSet = toSet; - toMap = toMap; - first = first; - firstOrNull = firstOrNull; - last = last; - lastOrNull = lastOrNull; - find = firstOrNull; - findLast = lastOrNull; - all = all; - any = any; - none = none; - count = count; - contains = contains; - indexOf = indexOf; - indexOfFirst = indexOfFirst; - indexOfLast = indexOfLast; - elementAt = elementAt; - elementAtOrNull = elementAtOrNull; - elementAtOrElse = elementAtOrElse; - joinTo = joinToString; - joinToString = joinToString; - single = single; - singleOrNull = singleOrNull; - associate = associate; - associateBy = associateBy; - groupBy = groupBy; - reduce = reduce; - reduceIndexed = reduceIndexed; - fold = fold; - foldIndexed = foldIndexed; - partition = partition; - chunk = chunk; - plus = plus; - minus = minus; - zip = zip; - unzip = unzip; - sum = sum; - sumBy = sumBy; - average = average; - max = max; - maxBy = maxBy; - maxWith = maxWith; - min = min; - minBy = minBy; - minWith = minWith; - asIterable = asIterable; - merge = merge; +function applyMixins(derivedCtor: any, baseCtors: any[]) { + baseCtors.forEach(baseCtor => { + Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { + derivedCtor.prototype[name] = baseCtor.prototype[name]; + }); + }); } export function sequenceOf(...args: Array): Sequence { @@ -174,18 +127,30 @@ export function asSequence(iterable: Iterable): Sequence { if (iterable[Symbol.iterator] == null) { throw new Error("Cannot create sequence for non-iterable input: " + iterable); } - return new Sequence(new IterableIterator(iterable)); + return createSequence(new IterableIterator(iterable)); +} + +export function createSequence(iterator: SequenceIterator): Sequence { + return new SequenceImpl(iterator) as any; +} + +export function isSequence(object: any): object is Sequence { + return object instanceof SequenceImpl; +} + +export function extendSequence(mixin: { new(): any }) { + applyMixins(SequenceImpl, [mixin]); } export function generateSequence(nextFunction: () => T | null | undefined): Sequence; -export function generateSequence(seedFunction: () => T | null | undefined, nextFunction: (item: T) => T | null | undefined): Sequence; -export function generateSequence(seed: T | null | undefined, nextFunction: (item: T) => T | null | undefined): Sequence; +export function generateSequence(seedFunction: () => T | null | undefined, nextFunction: (item: T) => T | null | undefined): Sequence; +export function generateSequence(seed: T | null | undefined, nextFunction: (item: T) => T | null | undefined): Sequence; export function generateSequence(a: any, b?: any): Sequence { if (typeof a === "function" && b == null) { - return new Sequence(new GeneratorIterator(a)); + return createSequence(new GeneratorIterator(a)); } const seed = typeof a === "function" ? a() : a; return seed != null - ? new Sequence(new GeneratorSeedIterator(seed, b)) + ? createSequence(new GeneratorSeedIterator(seed, b)) : emptySequence(); -} \ No newline at end of file +} diff --git a/src/all.ts b/src/all.ts index f96a7b5..76af63c 100644 --- a/src/all.ts +++ b/src/all.ts @@ -1,19 +1,21 @@ import Sequence from "./Sequence"; -/** - * Returns `true` if all elements match the given `predicate`. - * - * @param {(T) => boolean} predicate - * @returns {boolean} - */ -function all(this: Sequence, predicate: (T) => boolean): boolean { - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (!predicate(item)) { - return false; +export class All { + + /** + * Returns `true` if all elements match the given `predicate`. + * + * @param {(T) => boolean} predicate + * @returns {boolean} + */ + all(this: Sequence, predicate: (T) => boolean): boolean { + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (!predicate(item)) { + return false; + } } + return true; } - return true; -} -export default all; \ No newline at end of file +} \ No newline at end of file diff --git a/src/any.ts b/src/any.ts index 1ec42b0..8b0f119 100644 --- a/src/any.ts +++ b/src/any.ts @@ -1,22 +1,24 @@ import Sequence from "./Sequence"; -/** - * Returns `true` if at least one element match the given `predicate`. - * - * @param {(T) => boolean} predicate - * @returns {boolean} - */ -function any(this: Sequence, predicate?: (T) => boolean): boolean { - if (predicate == null) { - return this.iterator.hasNext(); - } - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (predicate(item)) { - return true; +export class Any { + + /** + * Returns `true` if at least one element match the given `predicate`. + * + * @param {(T) => boolean} predicate + * @returns {boolean} + */ + any(this: Sequence, predicate?: (T) => boolean): boolean { + if (predicate == null) { + return this.iterator.hasNext(); + } + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (predicate(item)) { + return true; + } } + return false; } - return false; -} -export default any; \ No newline at end of file +} \ No newline at end of file diff --git a/src/asIterable.ts b/src/asIterable.ts index 178630d..b325b7f 100644 --- a/src/asIterable.ts +++ b/src/asIterable.ts @@ -1,25 +1,27 @@ import Sequence from "./Sequence"; -/** - * Returns an iterable representation of the sequence. - * - * @returns {Iterable} - */ -function asIterable(this: Sequence): Iterable { - const iterator = this.iterator; - return { - [Symbol.iterator](): Iterator { - return { - next(): IteratorResult { - if (!iterator.hasNext()) { - return {done: true, value: undefined} as any as IteratorResult; +export class AsIterable { + + /** + * Returns an iterable representation of the sequence. + * + * @returns {Iterable} + */ + asIterable(this: Sequence): Iterable { + const iterator = this.iterator; + return { + [Symbol.iterator](): Iterator { + return { + next(): IteratorResult { + if (!iterator.hasNext()) { + return { done: true, value: undefined } as any as IteratorResult; + } + const item = iterator.next(); + return { done: false, value: item } as any as IteratorResult; } - const item = iterator.next(); - return {done: false, value: item} as any as IteratorResult; - } - }; - } - }; -} + }; + } + }; + } -export default asIterable; \ No newline at end of file +} \ No newline at end of file diff --git a/src/associate.ts b/src/associate.ts index 8ecb8be..3ab8178 100644 --- a/src/associate.ts +++ b/src/associate.ts @@ -1,20 +1,22 @@ import Sequence from "./Sequence"; -/** - * Transforms each element into a key-value pair and returns the results as map. In case of - * duplicate keys the last key-value pair overrides the other. - * - * @param {(value: T) => [K , V]} transform - * @returns {Map} - */ -function associate(this: Sequence, transform: (value: T) => [K, V]): Map { - const result = new Map(); - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - const pair = transform(item); - result.set(pair[0], pair[1]); +export class Associate { + + /** + * Transforms each element into a key-value pair and returns the results as map. In case of + * duplicate keys the last key-value pair overrides the other. + * + * @param {(value: T) => [K , V]} transform + * @returns {Map} + */ + associate(this: Sequence, transform: (value: T) => [K, V]): Map { + const result = new Map(); + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + const pair = transform(item); + result.set(pair[0], pair[1]); + } + return result; } - return result; -} -export default associate; \ No newline at end of file +} \ No newline at end of file diff --git a/src/associateBy.ts b/src/associateBy.ts index c56b9a8..1272ffa 100644 --- a/src/associateBy.ts +++ b/src/associateBy.ts @@ -1,27 +1,60 @@ import Sequence from "./Sequence"; -/** - * Returns a map consisting of the elements mapped by the given `keySelector`. The value - * can optionally be transformed into another value by specifying a `valueTransformer`. - * - * @param {(value: T) => K} keySelector - * @param {(value: T) => V} valueTransform - * @returns {Map} - */ -function associateBy(this: Sequence, - keySelector: (value: T) => K, - valueTransform?: (value: T) => V): Map { - const result = new Map(); - const transform = valueTransform != null - ? valueTransform - : (value: T) => value; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - const key = keySelector(item); - const value = transform(item); - result.set(key, value); +export class AssociateBy { + + /** + * Returns a map consisting of the elements mapped by the given `keySelector`. + * + * @param {(value: T) => K} keySelector + * @returns {Map} + */ + associateBy(keySelector: (value: T) => K): Map; + + /** + * Returns a map consisting of the elements indexed by the given `key`. + * + * @param {K} key + * @returns {Map} + */ + associateBy(key: K): Map; + + /** + * Returns a map consisting of the elements mapped by the given `keySelector`. The value + * is transformed into another value by the `valueTransformer`. + * + * @param {(value: T) => K} keySelector + * @param {(value: T) => V} valueTransformer + * @returns {Map} + */ + associateBy(keySelector: (value: T) => K, valueTransformer: (value: T) => V): Map; + + /** + * Returns a map consisting of the elements indexed by the given `key`. The value + * is transformed into another value by the `valueTransformer`. + * + * @param {K} key + * @param {(value: T) => V} valueTransformer + * @returns {Map} + */ + associateBy(key: K, valueTransformer: (value: T) => V): Map; + + associateBy(this: Sequence, + keyOrSelector: any, + valueTransform?: (value: T) => V): Map { + const selector = typeof keyOrSelector === "function" + ? keyOrSelector + : (value: T) => value[keyOrSelector as keyof T]; + const result = new Map(); + const transform = valueTransform != null + ? valueTransform + : (value: T) => value; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + const key = selector(item); + const value = transform(item); + result.set(key, value); + } + return result; } - return result; -} -export default associateBy; \ No newline at end of file +} \ No newline at end of file diff --git a/src/average.ts b/src/average.ts index f1fcec2..40a3ba1 100644 --- a/src/average.ts +++ b/src/average.ts @@ -1,20 +1,22 @@ import Sequence from "./Sequence"; -/** - * Returns the average of all numbers of the sequence or `NaN` if the sequence is empty. - * - * @returns {number} - */ -function average(this: Sequence): number { - let sum = 0; - let count = 0; - while (this.iterator.hasNext()) { - sum += this.iterator.next(); - count++; +export class Average { + + /** + * Returns the average of all numbers of the sequence or `NaN` if the sequence is empty. + * + * @returns {number} + */ + average(this: Sequence): number { + let sum = 0; + let count = 0; + while (this.iterator.hasNext()) { + sum += this.iterator.next(); + count++; + } + return count === 0 + ? Number.NaN + : sum / count; } - return count === 0 - ? Number.NaN - : sum / count; -} -export default average; \ No newline at end of file +} \ No newline at end of file diff --git a/src/chunk.ts b/src/chunk.ts index 5a06bf5..bcd0a59 100644 --- a/src/chunk.ts +++ b/src/chunk.ts @@ -1,29 +1,31 @@ import Sequence from "./Sequence"; -/** - * Splits the elements of the sequence into arrays which length is determined by - * the given `chunkSize` and returns all chunks as array. - * - * @param {number} chunkSize - * @returns {Array>} - */ -function chunk(this: Sequence, chunkSize: number): Array> { - if (chunkSize < 1) { - throw new Error("chunkSize must be > 0 but is " + chunkSize); - } - const result: Array> = []; - let index = 0; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - const chunkIndex = Math.floor(index / chunkSize); - if (result[chunkIndex] == null) { - result[chunkIndex] = [item]; - } else { - result[chunkIndex].push(item); +export class Chunk { + + /** + * Splits the elements of the sequence into arrays which length is determined by + * the given `chunkSize` and returns all chunks as array. + * + * @param {number} chunkSize + * @returns {Array>} + */ + chunk(this: Sequence, chunkSize: number): Array> { + if (chunkSize < 1) { + throw new Error("chunkSize must be > 0 but is " + chunkSize); + } + const result: Array> = []; + let index = 0; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + const chunkIndex = Math.floor(index / chunkSize); + if (result[chunkIndex] == null) { + result[chunkIndex] = [item]; + } else { + result[chunkIndex].push(item); + } + index++; } - index++; + return result; } - return result; -} -export default chunk; \ No newline at end of file +} \ No newline at end of file diff --git a/src/contains.ts b/src/contains.ts index ace4b44..f29db19 100644 --- a/src/contains.ts +++ b/src/contains.ts @@ -1,19 +1,21 @@ import Sequence from "./Sequence"; -/** - * Returns `true` if the sequence contains the given `element`. - * - * @param {T} element - * @returns {boolean} - */ -function contains(this: Sequence, element: T): boolean { - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (element === item) { - return true; +export class Contains { + + /** + * Returns `true` if the sequence contains the given `element`. + * + * @param {T} element + * @returns {boolean} + */ + contains(this: Sequence, element: T): boolean { + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (element === item) { + return true; + } } + return false; } - return false; -} -export default contains; \ No newline at end of file +} \ No newline at end of file diff --git a/src/count.ts b/src/count.ts index dc1624e..784df78 100644 --- a/src/count.ts +++ b/src/count.ts @@ -1,28 +1,30 @@ import Sequence from "./Sequence"; -/** - * Returns the number of elements of this sequence. If `predicate` is present, returns - * the number of elements matching the given `predicate`. - * - * @param {(T) => boolean} predicate - * @returns {number} - */ -function count(this: Sequence, predicate?: (T) => boolean): number { - let num = 0; - if (predicate == null) { - while (this.iterator.hasNext()) { - this.iterator.next(); - num++; - } - } else { - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (predicate(item)) { +export class Count { + + /** + * Returns the number of elements of this sequence. If `predicate` is present, returns + * the number of elements matching the given `predicate`. + * + * @param {(T) => boolean} predicate + * @returns {number} + */ + count(this: Sequence, predicate?: (T) => boolean): number { + let num = 0; + if (predicate == null) { + while (this.iterator.hasNext()) { + this.iterator.next(); num++; } + } else { + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (predicate(item)) { + num++; + } + } } + return num; } - return num; -} -export default count; \ No newline at end of file +} \ No newline at end of file diff --git a/src/distinct.ts b/src/distinct.ts index e54c229..3e1b177 100644 --- a/src/distinct.ts +++ b/src/distinct.ts @@ -1,4 +1,4 @@ -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; import SequenceIterator from "./SequenceIterator"; class DistinctIterator implements SequenceIterator { @@ -36,13 +36,15 @@ class DistinctIterator implements SequenceIterator { } } -/** - * Returns a new sequence which discards all duplicate elements. - * - * @returns {Sequence} - */ -function distinct(this: Sequence): Sequence { - return new Sequence(new DistinctIterator(this.iterator)); -} +export class Distinct { + + /** + * Returns a new sequence which discards all duplicate elements. + * + * @returns {Sequence} + */ + distinct(this: Sequence): Sequence { + return createSequence(new DistinctIterator(this.iterator)); + } -export default distinct; \ No newline at end of file +} \ No newline at end of file diff --git a/src/distinctBy.ts b/src/distinctBy.ts index 2a7690d..2538660 100644 --- a/src/distinctBy.ts +++ b/src/distinctBy.ts @@ -1,4 +1,4 @@ -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; import SequenceIterator from "./SequenceIterator"; class DistinctByIterator implements SequenceIterator { @@ -40,15 +40,17 @@ class DistinctByIterator implements SequenceIterator { } } -/** - * Returns a new sequence which discards all elements with duplicate items determined - * by the given `selector`. - * - * @param {(item: T) => K} selector - * @returns {Sequence} - */ -function distinctBy(this: Sequence, selector: (item: T) => K): Sequence { - return new Sequence(new DistinctByIterator(this.iterator, selector)); -} +export class DistinctBy { + + /** + * Returns a new sequence which discards all elements with duplicate items determined + * by the given `selector`. + * + * @param {(item: T) => K} selector + * @returns {Sequence} + */ + distinctBy(this: Sequence, selector: (item: T) => K): Sequence { + return createSequence(new DistinctByIterator(this.iterator, selector)); + } -export default distinctBy; +} \ No newline at end of file diff --git a/src/drop.ts b/src/drop.ts index d1da009..b2bc82b 100644 --- a/src/drop.ts +++ b/src/drop.ts @@ -1,15 +1,17 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence which discards the first `n` elements; - * - * @param {number} n - * @returns {Sequence} - */ -function drop(this: Sequence, n: number): Sequence { - return this.withIndex() - .dropWhile(it => it.index < n) - .map(it => it.value); -} +export class Drop { -export default drop; \ No newline at end of file + /** + * Returns a new sequence which discards the first `n` elements; + * + * @param {number} n + * @returns {Sequence} + */ + drop(this: Sequence, n: number): Sequence { + return this.withIndex() + .dropWhile(it => it.index < n) + .map(it => it.value); + } + +} \ No newline at end of file diff --git a/src/dropWhile.ts b/src/dropWhile.ts index 126e6f6..9502b9a 100644 --- a/src/dropWhile.ts +++ b/src/dropWhile.ts @@ -1,4 +1,4 @@ -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; import SequenceIterator from "./SequenceIterator"; class DropWhileIterator implements SequenceIterator { @@ -44,14 +44,16 @@ class DropWhileIterator implements SequenceIterator { } } -/** - * Drops all elements of the sequence as long as the given `predicate` evaluates to true. - * - * @param {(item: T) => boolean} predicate - * @returns {Sequence} - */ -function dropWhile(this: Sequence, predicate: (item: T) => boolean): Sequence { - return new Sequence(new DropWhileIterator(this.iterator, predicate)); -} +export class DropWhile { + + /** + * Drops all elements of the sequence as long as the given `predicate` evaluates to true. + * + * @param {(item: T) => boolean} predicate + * @returns {Sequence} + */ + dropWhile(this: Sequence, predicate: (item: T) => boolean): Sequence { + return createSequence(new DropWhileIterator(this.iterator, predicate)); + } -export default dropWhile; \ No newline at end of file +} \ No newline at end of file diff --git a/src/elementAt.ts b/src/elementAt.ts index 05b703e..9515267 100644 --- a/src/elementAt.ts +++ b/src/elementAt.ts @@ -1,22 +1,24 @@ import Sequence from "./Sequence"; -/** - * Returns the element at position `index` (zero-based) or throws an error if `index` - * is out of bounds. - * - * @param {number} index - * @returns {T} - */ -function elementAt(this: Sequence, index: number): T { - let i = 0; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (i === index) { - return item; +export class ElementAt { + + /** + * Returns the element at position `index` (zero-based) or throws an error if `index` + * is out of bounds. + * + * @param {number} index + * @returns {T} + */ + elementAt(this: Sequence, index: number): T { + let i = 0; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (i === index) { + return item; + } + i++; } - i++; + throw new Error("Index out of bounds: " + index); } - throw new Error("Index out of bounds: " + index); -} -export default elementAt; \ No newline at end of file +} \ No newline at end of file diff --git a/src/elementAtOrElse.ts b/src/elementAtOrElse.ts index 392d320..1878f33 100644 --- a/src/elementAtOrElse.ts +++ b/src/elementAtOrElse.ts @@ -1,23 +1,25 @@ import Sequence from "./Sequence"; -/** - * Returns the element at position `index` (zero-based). If `index` is out of bounds returns - * the result of the given `defaultValue` function. - * - * @param {number} index - * @param defaultValue - * @returns {T} - */ -function elementAtOrElse(this: Sequence, index: number, defaultValue: (index: number) => T): T { - let i = 0; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (i === index) { - return item; +export class ElementAtOrElse { + + /** + * Returns the element at position `index` (zero-based). If `index` is out of bounds returns + * the result of the given `defaultValue` function. + * + * @param {number} index + * @param defaultValue + * @returns {T} + */ + elementAtOrElse(this: Sequence, index: number, defaultValue: (index: number) => T): T { + let i = 0; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (i === index) { + return item; + } + i++; } - i++; + return defaultValue(index); } - return defaultValue(index); -} -export default elementAtOrElse; \ No newline at end of file +} \ No newline at end of file diff --git a/src/elementAtOrNull.ts b/src/elementAtOrNull.ts index c4bcda1..58b3fd6 100644 --- a/src/elementAtOrNull.ts +++ b/src/elementAtOrNull.ts @@ -1,22 +1,24 @@ import Sequence from "./Sequence"; -/** - * Returns the element at position `index` (zero-based) or `null` if `index` - * is out of bounds. - * - * @param {number} index - * @returns {T} - */ -function elementAtOrNull(this: Sequence, index: number): T | null { - let i = 0; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (i === index) { - return item; +export class ElementAtOrNull { + + /** + * Returns the element at position `index` (zero-based) or `null` if `index` + * is out of bounds. + * + * @param {number} index + * @returns {T} + */ + elementAtOrNull(this: Sequence, index: number): T | null { + let i = 0; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (i === index) { + return item; + } + i++; } - i++; + return null; } - return null; -} -export default elementAtOrNull; \ No newline at end of file +} \ No newline at end of file diff --git a/src/filter.ts b/src/filter.ts index 24762fe..3377e72 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -1,5 +1,5 @@ import SequenceIterator from "./SequenceIterator"; -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; class FilterIterator implements SequenceIterator { private nextItem: T | undefined; @@ -37,14 +37,16 @@ class FilterIterator implements SequenceIterator { } } -/** - * Returns a new sequence consisting of all elements that match the given `predicate`. - * - * @param {(T) => boolean} predicate - * @returns {Sequence} - */ -function filter(this: Sequence, predicate: (T) => boolean): Sequence { - return new Sequence(new FilterIterator(predicate, this.iterator)); -} +export class Filter { + + /** + * Returns a new sequence consisting of all elements that match the given `predicate`. + * + * @param {(T) => boolean} predicate + * @returns {Sequence} + */ + filter(this: Sequence, predicate: (T) => boolean): Sequence { + return createSequence(new FilterIterator(predicate, this.iterator)); + } -export default filter; \ No newline at end of file +} \ No newline at end of file diff --git a/src/filterIndexed.ts b/src/filterIndexed.ts index 546b891..30b97a3 100644 --- a/src/filterIndexed.ts +++ b/src/filterIndexed.ts @@ -1,15 +1,17 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence consisting of all elements that match the given `predicate`. - * - * @param {(index: number, value: T) => boolean} predicate - * @returns {Sequence} - */ -function filterIndexed(this: Sequence, predicate: (index: number, value: T) => boolean): Sequence { - return this.withIndex() - .filter(it => predicate(it.index, it.value)) - .map(it => it.value); -} +export class FilterIndexed { -export default filterIndexed; \ No newline at end of file + /** + * Returns a new sequence consisting of all elements that match the given `predicate`. + * + * @param {(index: number, value: T) => boolean} predicate + * @returns {Sequence} + */ + filterIndexed(this: Sequence, predicate: (index: number, value: T) => boolean): Sequence { + return this.withIndex() + .filter(it => predicate(it.index, it.value)) + .map(it => it.value); + } + +} \ No newline at end of file diff --git a/src/filterNot.ts b/src/filterNot.ts index dad2b9a..87bf57d 100644 --- a/src/filterNot.ts +++ b/src/filterNot.ts @@ -1,13 +1,15 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence consisting of all elements that don't match the given `predicate`. - * - * @param {(value: T) => boolean} predicate - * @returns {Sequence} - */ -function filterNot(this: Sequence, predicate: (value: T) => boolean): Sequence { - return this.filter((value: T) => !predicate(value)); -} +export class FilterNot { -export default filterNot; \ No newline at end of file + /** + * Returns a new sequence consisting of all elements that don't match the given `predicate`. + * + * @param {(value: T) => boolean} predicate + * @returns {Sequence} + */ + filterNot(this: Sequence, predicate: (value: T) => boolean): Sequence { + return this.filter((value: T) => !predicate(value)); + } + +} \ No newline at end of file diff --git a/src/filterNotNull.ts b/src/filterNotNull.ts index dc595c2..a33a0f5 100644 --- a/src/filterNotNull.ts +++ b/src/filterNotNull.ts @@ -1,12 +1,14 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence consisting of all non-null elements. - * - * @returns {Sequence} - */ -function filterNotNull(this: Sequence): Sequence { - return this.filter(it => it !== null); -} +export class FilterNotNull { -export default filterNotNull; \ No newline at end of file + /** + * Returns a new sequence consisting of all non-null elements. + * + * @returns {Sequence} + */ + filterNotNull(this: Sequence): Sequence { + return this.filter(it => it !== null); + } + +} \ No newline at end of file diff --git a/src/first.ts b/src/first.ts index 0e443f0..c04ed43 100644 --- a/src/first.ts +++ b/src/first.ts @@ -1,20 +1,22 @@ import Sequence from "./Sequence"; -/** - * Returns the first element of the sequence or the first element matching `predicate` if present, otherwise throws - * an error. - * - * @param {(T) => boolean} predicate - * @returns {T} - */ -function first(this: Sequence, predicate?: (T) => boolean): T { - if (predicate != null) { - return this.filter(predicate).first(); - } - if (!this.iterator.hasNext()) { - throw new Error("No such element"); +export class First { + + /** + * Returns the first element of the sequence or the first element matching `predicate` if present, otherwise throws + * an error. + * + * @param {(T) => boolean} predicate + * @returns {T} + */ + first(this: Sequence, predicate?: (T) => boolean): T { + if (predicate != null) { + return this.filter(predicate).first(); + } + if (!this.iterator.hasNext()) { + throw new Error("No such element"); + } + return this.iterator.next(); } - return this.iterator.next(); -} -export default first; \ No newline at end of file +} \ No newline at end of file diff --git a/src/firstOrNull.ts b/src/firstOrNull.ts index 887d284..ae5399e 100644 --- a/src/firstOrNull.ts +++ b/src/firstOrNull.ts @@ -1,18 +1,30 @@ import Sequence from "./Sequence"; -/** - * Returns the first element of the sequence or the first element matching `predicate` if present, otherwise returns `null`. - * - * @param {(T) => boolean} predicate - * @returns {T} - */ -function firstOrNull(this: Sequence, predicate?: (T) => boolean): T | null { - if (predicate != null) { - return this.filter(predicate).firstOrNull(); +export class FirstOrNull { + + /** + * Returns the first element of the sequence or the first element matching `predicate` if present, otherwise returns `null`. + * + * @param {(T) => boolean} predicate + * @returns {T} + */ + firstOrNull(this: Sequence, predicate?: (T) => boolean): T | null { + if (predicate != null) { + return this.filter(predicate).firstOrNull(); + } + return this.iterator.hasNext() + ? this.iterator.next() + : null; + } + + /** + * Returns the first element of the sequence or the first element matching `predicate` if present, otherwise returns `null`. + * + * @param {(T) => boolean} predicate + * @returns {T} + */ + find(this: Sequence, predicate?: (T) => boolean): T | null { + return this.firstOrNull(predicate); } - return this.iterator.hasNext() - ? this.iterator.next() - : null; -} -export default firstOrNull; \ No newline at end of file +} \ No newline at end of file diff --git a/src/flatMap.ts b/src/flatMap.ts index cbd503d..ebbc62a 100644 --- a/src/flatMap.ts +++ b/src/flatMap.ts @@ -1,5 +1,5 @@ import SequenceIterator from "./SequenceIterator"; -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; class FlatMapIterator implements SequenceIterator { private current: SequenceIterator | undefined; @@ -37,14 +37,16 @@ class FlatMapIterator implements SequenceIterator { } } -/** - * Transforms each element into a sequence of items and returns a flat single sequence of all those items. - * - * @param {(value: S) => Sequence} transform - * @returns {Sequence} - */ -function flatMap(this: Sequence, transform: (value: S) => Sequence): Sequence { - return new Sequence(new FlatMapIterator(transform, this.iterator)); -} +export class FlatMap { + + /** + * Transforms each element into a sequence of items and returns a flat single sequence of all those items. + * + * @param {(value: S) => Sequence} transform + * @returns {Sequence} + */ + flatMap(this: Sequence, transform: (value: S) => Sequence): Sequence { + return createSequence(new FlatMapIterator(transform, this.iterator)); + } -export default flatMap; \ No newline at end of file +} \ No newline at end of file diff --git a/src/flatten.ts b/src/flatten.ts index ee92fb5..9754b2f 100644 --- a/src/flatten.ts +++ b/src/flatten.ts @@ -1,18 +1,20 @@ -import Sequence, {asSequence} from "./Sequence"; +import Sequence, {asSequence, isSequence} from "./Sequence"; -/** - * Returns a single flat sequence of all the items from all sequences or iterables. - * - * @returns {Sequence} - */ -function flatten(this: Sequence | Iterable>): Sequence { - return this.flatMap(it => { - if (it instanceof Sequence) { - return it; - } else { - return asSequence(it); - } - }); -} +export class Flatten { -export default flatten; \ No newline at end of file + /** + * Returns a single flat sequence of all the items from all sequences or iterables. + * + * @returns {Sequence} + */ + flatten(this: Sequence | Iterable>): Sequence { + return this.flatMap(it => { + if (isSequence(it)) { + return it; + } else { + return asSequence(it); + } + }); + } + +} \ No newline at end of file diff --git a/src/fold.ts b/src/fold.ts index a59523e..6867b31 100644 --- a/src/fold.ts +++ b/src/fold.ts @@ -1,21 +1,23 @@ import Sequence from "./Sequence"; -/** - * Accumulates all elements of the sequence into a single result by applying the given `operation` starting with - * the `initial` value. The result of the last operation will be passed as accumulated value to the next invocation - * of the operation until all elements of the sequence are processed. - * - * @param {R} initial - * @param {(acc: R, element: T) => R} operation - * @returns {R} - */ -function fold(this: Sequence, initial: R, operation: (acc: R, element: T) => R): R { - let result = initial; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - result = operation(result, item); +export class Fold { + + /** + * Accumulates all elements of the sequence into a single result by applying the given `operation` starting with + * the `initial` value. The result of the last operation will be passed as accumulated value to the next invocation + * of the operation until all elements of the sequence are processed. + * + * @param {R} initial + * @param {(acc: R, element: T) => R} operation + * @returns {R} + */ + fold(this: Sequence, initial: R, operation: (acc: R, element: T) => R): R { + let result = initial; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + result = operation(result, item); + } + return result; } - return result; -} -export default fold; \ No newline at end of file +} \ No newline at end of file diff --git a/src/foldIndexed.ts b/src/foldIndexed.ts index 0c2404d..f50dde6 100644 --- a/src/foldIndexed.ts +++ b/src/foldIndexed.ts @@ -1,24 +1,26 @@ import Sequence from "./Sequence"; -/** - * Accumulates all elements of the sequence into a single result by applying the given `operation` starting with - * the `initial` value. The result of the last operation will be passed as accumulated value to the next invocation - * of the operation as well as the `index` of the current element (zero-based) until all elements of the sequence - * are processed. - * - * @param {R} initial - * @param {(index: number, acc: R, element: T) => R} operation - * @returns {R} - */ -function foldIndexed(this: Sequence, initial: R, operation: (index: number, acc: R, element: T) => R): R { - let result = initial; - let index = 0; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - result = operation(index, result, item); - index++; +export class FoldIndexed { + + /** + * Accumulates all elements of the sequence into a single result by applying the given `operation` starting with + * the `initial` value. The result of the last operation will be passed as accumulated value to the next invocation + * of the operation as well as the `index` of the current element (zero-based) until all elements of the sequence + * are processed. + * + * @param {R} initial + * @param {(index: number, acc: R, element: T) => R} operation + * @returns {R} + */ + foldIndexed(this: Sequence, initial: R, operation: (index: number, acc: R, element: T) => R): R { + let result = initial; + let index = 0; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + result = operation(index, result, item); + index++; + } + return result; } - return result; -} -export default foldIndexed; \ No newline at end of file +} \ No newline at end of file diff --git a/src/forEach.ts b/src/forEach.ts index 5ae1b21..89fef9e 100644 --- a/src/forEach.ts +++ b/src/forEach.ts @@ -1,15 +1,17 @@ import Sequence from "./Sequence"; -/** - * Performs the given `action` (side-effect) for each element of the sequence. - * - * @param {(T) => void} action - */ -function forEach(this: Sequence, action: (T) => void) { - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - action(item); +export class ForEach { + + /** + * Performs the given `action` (side-effect) for each element of the sequence. + * + * @param {(T) => void} action + */ + forEach(this: Sequence, action: (T) => void) { + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + action(item); + } } -} -export default forEach; \ No newline at end of file +} \ No newline at end of file diff --git a/src/forEachIndexed.ts b/src/forEachIndexed.ts index 5c57ad4..920746a 100644 --- a/src/forEachIndexed.ts +++ b/src/forEachIndexed.ts @@ -1,14 +1,16 @@ import Sequence from "./Sequence"; -/** - * Performs the given `action` (side-effect) for each element of the sequence and passes the `index` of the current - * element (zero-based). - * - * @param {(index: number, value: T) => void} action - */ -function forEachIndexed(this: Sequence, action: (index: number, value: T) => void) { - this.withIndex() - .forEach(it => action(it.index, it.value)); -} +export class ForEachIndexed { -export default forEachIndexed; \ No newline at end of file + /** + * Performs the given `action` (side-effect) for each element of the sequence and passes the `index` of the current + * element (zero-based). + * + * @param {(index: number, value: T) => void} action + */ + forEachIndexed(this: Sequence, action: (index: number, value: T) => void) { + this.withIndex() + .forEach(it => action(it.index, it.value)); + } + +} \ No newline at end of file diff --git a/src/groupBy.ts b/src/groupBy.ts index 15f667f..74cc10a 100644 --- a/src/groupBy.ts +++ b/src/groupBy.ts @@ -1,24 +1,26 @@ import Sequence from "./Sequence"; -/** - * Groups all elements of the sequence into a map. Keys are determined by the given `keySelector` function. - * - * @param {(value: T) => K} keySelector - * @returns {Map>} - */ -function groupBy(this: Sequence, keySelector: (value: T) => K): Map> { - const result = new Map>(); - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - const key = keySelector(item); - const array = result.get(key); - if (array == null) { - result.set(key, [item]); - } else { - array.push(item); +export class GroupBy { + + /** + * Groups all elements of the sequence into a map. Keys are determined by the given `keySelector` function. + * + * @param {(value: T) => K} keySelector + * @returns {Map>} + */ + groupBy(this: Sequence, keySelector: (value: T) => K): Map> { + const result = new Map>(); + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + const key = keySelector(item); + const array = result.get(key); + if (array == null) { + result.set(key, [item]); + } else { + array.push(item); + } } + return result; } - return result; -} -export default groupBy; \ No newline at end of file +} \ No newline at end of file diff --git a/src/indexOf.ts b/src/indexOf.ts index 022971d..9a0064b 100644 --- a/src/indexOf.ts +++ b/src/indexOf.ts @@ -1,21 +1,23 @@ import Sequence from "./Sequence"; -/** - * Returns the zero-based index of the given `element` or -1 if the sequence does not contain the element. - * - * @param {T} element - * @returns {number} - */ -function indexOf(this: Sequence, element: T): number { - let index = 0; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (item === element) { - return index; +export class IndexOf { + + /** + * Returns the zero-based index of the given `element` or -1 if the sequence does not contain the element. + * + * @param {T} element + * @returns {number} + */ + indexOf(this: Sequence, element: T): number { + let index = 0; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (item === element) { + return index; + } + index++; } - index++; + return -1; } - return -1; -} -export default indexOf; \ No newline at end of file +} \ No newline at end of file diff --git a/src/indexOfFirst.ts b/src/indexOfFirst.ts index a3d9b51..b20be76 100644 --- a/src/indexOfFirst.ts +++ b/src/indexOfFirst.ts @@ -1,22 +1,24 @@ import Sequence from "./Sequence"; -/** - * Returns the zero-based index of the first element matching the given `predicate` or -1 if no element matches - * the predicate. - * - * @param {(value: T) => boolean} predicate - * @returns {number} - */ -function indexOfFirst(this: Sequence, predicate: (value: T) => boolean): number { - let index = 0; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (predicate(item)) { - return index; +export class IndexOfFirst { + + /** + * Returns the zero-based index of the first element matching the given `predicate` or -1 if no element matches + * the predicate. + * + * @param {(value: T) => boolean} predicate + * @returns {number} + */ + indexOfFirst(this: Sequence, predicate: (value: T) => boolean): number { + let index = 0; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (predicate(item)) { + return index; + } + index++; } - index++; + return -1; } - return -1; -} -export default indexOfFirst; \ No newline at end of file +} diff --git a/src/indexOfLast.ts b/src/indexOfLast.ts index 295a2b1..608ca48 100644 --- a/src/indexOfLast.ts +++ b/src/indexOfLast.ts @@ -1,23 +1,25 @@ import Sequence from "./Sequence"; -/** - * Returns the zero-based index of the last element matching the given `predicate` or -1 if no element matches - * the predicate. - * - * @param {(value: T) => boolean} predicate - * @returns {number} - */ -function indexOfLast(this: Sequence, predicate: (value: T) => boolean): number { - let index = 0; - let result = -1; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (predicate(item)) { - result = index; +export class IndexOfLast { + + /** + * Returns the zero-based index of the last element matching the given `predicate` or -1 if no element matches + * the predicate. + * + * @param {(value: T) => boolean} predicate + * @returns {number} + */ + indexOfLast(this: Sequence, predicate: (value: T) => boolean): number { + let index = 0; + let result = -1; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (predicate(item)) { + result = index; + } + index++; } - index++; + return result; } - return result; -} -export default indexOfLast; \ No newline at end of file +} \ No newline at end of file diff --git a/src/joinToString.ts b/src/joinToString.ts index e0d1a4a..7a62ac3 100644 --- a/src/joinToString.ts +++ b/src/joinToString.ts @@ -20,47 +20,59 @@ const defaults = { transform: undefined }; -/** - * Joins all elements of the sequence into a string with the given configuration. - * - * @param {JoinConfig} config - * @returns {string} - */ -function joinToString(this: Sequence, config: JoinConfig = defaults): string { - const { - value = defaults.value, - separator = defaults.separator, - prefix = defaults.prefix, - postfix = defaults.postfix, - limit = defaults.limit, - truncated = defaults.truncated, - transform = defaults.transform - } = config; +export class JoinToString { - let result = `${value}${prefix}`; - let count = 0; + /** + * Joins all elements of the sequence into a string with the given configuration. + * + * @param {JoinConfig} config + * @returns {string} + */ + joinToString(this: Sequence, config: JoinConfig = defaults): string { + const { + value = defaults.value, + separator = defaults.separator, + prefix = defaults.prefix, + postfix = defaults.postfix, + limit = defaults.limit, + truncated = defaults.truncated, + transform = defaults.transform + } = config; - while (this.iterator.hasNext()) { - count++; - const item = this.iterator.next(); - if (count > 1) { - result += separator; + let result = `${value}${prefix}`; + let count = 0; + + while (this.iterator.hasNext()) { + count++; + const item = this.iterator.next(); + if (count > 1) { + result += separator; + } + if (limit < 0 || count <= limit) { + result += transform != null + ? transform(item) + : String(item); + } else { + break; + } } - if (limit < 0 || count <= limit) { - result += transform != null - ? transform(item) - : String(item); - } else { - break; + + if (limit >= 0 && count > limit) { + result += truncated; } - } - if (limit >= 0 && count > limit) { - result += truncated; + result += postfix; + return result; } - result += postfix; - return result; -} + /** + * Joins all elements of the sequence into a string with the given configuration. + * + * @param {JoinConfig} config + * @returns {string} + */ + joinTo(this: Sequence, config: JoinConfig = defaults): string { + return this.joinToString(config); + } -export default joinToString; \ No newline at end of file +} \ No newline at end of file diff --git a/src/last.ts b/src/last.ts index 56536e0..587318d 100644 --- a/src/last.ts +++ b/src/last.ts @@ -1,24 +1,26 @@ import Sequence from "./Sequence"; -/** - * Returns the last element of the sequence or the last element matching `predicate` if present, otherwise throws - * an error. - * - * @param {(value: T) => boolean} predicate - * @returns {T} - */ -function last(this: Sequence, predicate?: (value: T) => boolean): T { - if (predicate != null) { - return this.filter(predicate).last(); - } - if (!this.iterator.hasNext()) { - throw new Error("No such element"); - } - let item: T; - while (this.iterator.hasNext()) { - item = this.iterator.next(); +export class Last { + + /** + * Returns the last element of the sequence or the last element matching `predicate` if present, otherwise throws + * an error. + * + * @param {(value: T) => boolean} predicate + * @returns {T} + */ + last(this: Sequence, predicate?: (value: T) => boolean): T { + if (predicate != null) { + return this.filter(predicate).last(); + } + if (!this.iterator.hasNext()) { + throw new Error("No such element"); + } + let item: T; + while (this.iterator.hasNext()) { + item = this.iterator.next(); + } + return item!; } - return item!; -} -export default last; \ No newline at end of file +} \ No newline at end of file diff --git a/src/lastOrNull.ts b/src/lastOrNull.ts index ebef7a2..5667ffc 100644 --- a/src/lastOrNull.ts +++ b/src/lastOrNull.ts @@ -1,20 +1,32 @@ import Sequence from "./Sequence"; -/** - * Returns the last element of the sequence or the last element matching `predicate` if present, otherwise returns `null`. - * - * @param {(value: T) => boolean} predicate - * @returns {T} - */ -function lastOrNull(this: Sequence, predicate?: (value: T) => boolean): T | null { - if (predicate != null) { - return this.filter(predicate).lastOrNull(); +export class LastOrNull { + + /** + * Returns the last element of the sequence or the last element matching `predicate` if present, otherwise returns `null`. + * + * @param {(value: T) => boolean} predicate + * @returns {T} + */ + lastOrNull(this: Sequence, predicate?: (value: T) => boolean): T | null { + if (predicate != null) { + return this.filter(predicate).lastOrNull(); + } + let item: T | null = null; + while (this.iterator.hasNext()) { + item = this.iterator.next(); + } + return item; } - let item: T | null = null; - while (this.iterator.hasNext()) { - item = this.iterator.next(); + + /** + * Returns the last element of the sequence or the last element matching `predicate` if present, otherwise returns `null`. + * + * @param {(value: T) => boolean} predicate + * @returns {T} + */ + findLast(this: Sequence, predicate?: (value: T) => boolean): T | null { + return this.lastOrNull(predicate); } - return item; -} -export default lastOrNull; \ No newline at end of file +} \ No newline at end of file diff --git a/src/map.ts b/src/map.ts index ff6fc06..7dba869 100644 --- a/src/map.ts +++ b/src/map.ts @@ -1,5 +1,5 @@ import SequenceIterator from "./SequenceIterator"; -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; class MapIterator implements SequenceIterator { constructor( @@ -17,14 +17,16 @@ class MapIterator implements SequenceIterator { } } -/** - * Transforms each element into another value by applying the given `transform` function and returns a new sequence. - * - * @param {(T) => S} transform - * @returns {Sequence} - */ -function map(this: Sequence, transform: (element: T) => S): Sequence { - return new Sequence(new MapIterator(transform, this.iterator)); -} +export class Map { + + /** + * Transforms each element into another value by applying the given `transform` function and returns a new sequence. + * + * @param {(T) => S} transform + * @returns {Sequence} + */ + map(this: Sequence, transform: (element: T) => S): Sequence { + return createSequence(new MapIterator(transform, this.iterator)); + } -export default map; \ No newline at end of file +} \ No newline at end of file diff --git a/src/mapIndexed.ts b/src/mapIndexed.ts index b233527..9cede3b 100644 --- a/src/mapIndexed.ts +++ b/src/mapIndexed.ts @@ -1,14 +1,16 @@ import Sequence from "./Sequence"; -/** - * Transforms each element into another value by applying the given `transform` function and returns a new sequence. - * - * @param {(index: number, value: T) => R} transform - * @returns {Sequence} - */ -function mapIndexed(this: Sequence, transform: (index: number, value: T) => R): Sequence { - return this.withIndex() - .map(it => transform(it.index, it.value)); -} +export class MapIndexed { -export default mapIndexed; \ No newline at end of file + /** + * Transforms each element into another value by applying the given `transform` function and returns a new sequence. + * + * @param {(index: number, value: T) => R} transform + * @returns {Sequence} + */ + mapIndexed(this: Sequence, transform: (index: number, value: T) => R): Sequence { + return this.withIndex() + .map(it => transform(it.index, it.value)); + } + +} \ No newline at end of file diff --git a/src/mapNotNull.ts b/src/mapNotNull.ts index bdea28c..cfd27ce 100644 --- a/src/mapNotNull.ts +++ b/src/mapNotNull.ts @@ -1,19 +1,21 @@ import Sequence, {emptySequence, sequenceOf} from "./Sequence"; -/** - * Transforms each element into another value by applying the given `transform` function and returns a new sequence. - * Transformations into `null` values are discarded. - * - * @param {(value: T) => R} transform - * @returns {Sequence} - */ -function mapNotNull(this: Sequence, transform: (value: T) => R | null): Sequence { - return this.flatMap((value: T) => { - const item = transform(value); - return item !== null - ? sequenceOf(item) - : emptySequence(); - }); -} +export class MapNotNull { -export default mapNotNull; \ No newline at end of file + /** + * Transforms each element into another value by applying the given `transform` function and returns a new sequence. + * Transformations into `null` values are discarded. + * + * @param {(value: T) => R} transform + * @returns {Sequence} + */ + mapNotNull(this: Sequence, transform: (value: T) => R | null): Sequence { + return this.flatMap((value: T) => { + const item = transform(value); + return item !== null + ? sequenceOf(item) + : emptySequence(); + }); + } + +} \ No newline at end of file diff --git a/src/max.ts b/src/max.ts index 184b89a..2136b99 100644 --- a/src/max.ts +++ b/src/max.ts @@ -1,19 +1,21 @@ import Sequence from "./Sequence"; -/** - * Returns the maximum element of the sequence or `null` if sequence is empty. - * - * @returns {T} - */ -function max(this: Sequence): T | null { - let result: T | null = null; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (result == null || item > result) { - result = item; +export class Max { + + /** + * Returns the maximum element of the sequence or `null` if sequence is empty. + * + * @returns {T} + */ + max(this: Sequence): T | null { + let result: T | null = null; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (result == null || item > result) { + result = item; + } } + return result; } - return result; -} -export default max; \ No newline at end of file +} \ No newline at end of file diff --git a/src/maxBy.ts b/src/maxBy.ts index 65b0f43..34a027e 100644 --- a/src/maxBy.ts +++ b/src/maxBy.ts @@ -1,24 +1,26 @@ import Sequence from "./Sequence"; -/** - * Returns the maximum element by comparing the results of the given `selector` function - * for each element of the sequence or `null` if the sequence is empty. - * - * @param {(value: T) => R} selector - * @returns {T} - */ -function maxBy(this: Sequence, selector: (value: T) => R): T | null { - let max: T | null = null; - let maxSelected: R | null = null; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - const value = selector(item); - if (maxSelected == null || value > maxSelected) { - maxSelected = value; - max = item; +export class MaxBy { + + /** + * Returns the maximum element by comparing the results of the given `selector` function + * for each element of the sequence or `null` if the sequence is empty. + * + * @param {(value: T) => R} selector + * @returns {T} + */ + maxBy(this: Sequence, selector: (value: T) => R): T | null { + let max: T | null = null; + let maxSelected: R | null = null; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + const value = selector(item); + if (maxSelected == null || value > maxSelected) { + maxSelected = value; + max = item; + } } + return max; } - return max; -} -export default maxBy; \ No newline at end of file +} \ No newline at end of file diff --git a/src/maxWith.ts b/src/maxWith.ts index db673fa..00d960b 100644 --- a/src/maxWith.ts +++ b/src/maxWith.ts @@ -1,20 +1,22 @@ import Sequence from "./Sequence"; -/** - * Returns the maximum element of the sequence by evaluating the given `compare` - * function or `null` if sequence is empty. - * - * @returns {T} - */ -function maxWith(this: Sequence, compare: (a: T, b: T) => number): T | null { - let max: T | null = null; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (max == null || compare(item, max) > 0) { - max = item; +export class MaxWith { + + /** + * Returns the maximum element of the sequence by evaluating the given `compare` + * function or `null` if sequence is empty. + * + * @returns {T} + */ + maxWith(this: Sequence, compare: (a: T, b: T) => number): T | null { + let max: T | null = null; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (max == null || compare(item, max) > 0) { + max = item; + } } + return max; } - return max; -} -export default maxWith; \ No newline at end of file +} \ No newline at end of file diff --git a/src/merge.ts b/src/merge.ts index ee9087d..c0a3053 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -1,37 +1,39 @@ -import Sequence, {asSequence} from "./Sequence"; +import Sequence, {asSequence, isSequence} from "./Sequence"; -/** - * Merges the elements of both sequences into a new sequence. Each element of this sequence is eventually replaced with - * an element of the other sequence by comparing results of the given `selector` function. If no value is found in the other - * sequence the element is retained. New elements of the other sequence are appended to the end of the new sequence or - * prepended to the start of the new sequence, if `prependNewValues` is set to `true`. - * - * @param {Sequence} other - * @param {(value: T) => S} selector - * @param prependNewValues - * @returns {Sequence} - */ -function merge(this: Sequence, other: Sequence | Iterable, selector: (value: T) => S, prependNewValues: boolean = false): Sequence { - let mergeValues = other instanceof Sequence - ? other.toArray() - : asSequence(other).toArray(); - const leftValues = this.toArray(); - const result = leftValues.map(left => { - const selected = selector(left); - const right = asSequence(mergeValues) - .find(it => selector(it) === selected); - if (right != null) { - mergeValues = mergeValues.filter(it => it !== right); - return right; +export class Merge { + + /** + * Merges the elements of both sequences into a new sequence. Each element of this sequence is eventually replaced with + * an element of the other sequence by comparing results of the given `selector` function. If no value is found in the other + * sequence the element is retained. New elements of the other sequence are appended to the end of the new sequence or + * prepended to the start of the new sequence, if `prependNewValues` is set to `true`. + * + * @param {Sequence} other + * @param {(value: T) => S} selector + * @param prependNewValues + * @returns {Sequence} + */ + merge(this: Sequence, other: Sequence | Iterable, selector: (value: T) => S, prependNewValues: boolean = false): Sequence { + let mergeValues = isSequence(other) + ? other.toArray() + : asSequence(other).toArray(); + const leftValues = this.toArray(); + const result = leftValues.map(left => { + const selected = selector(left); + const right = asSequence(mergeValues) + .find(it => selector(it) === selected); + if (right != null) { + mergeValues = mergeValues.filter(it => it !== right); + return right; + } else { + return left; + } + }); + if (prependNewValues) { + return asSequence([...mergeValues, ...result]); } else { - return left; + return asSequence([...result, ...mergeValues]); } - }); - if (prependNewValues) { - return asSequence([...mergeValues, ...result]); - } else { - return asSequence([...result, ...mergeValues]); } -} -export default merge; \ No newline at end of file +} \ No newline at end of file diff --git a/src/min.ts b/src/min.ts index 0cc193e..234f9f1 100644 --- a/src/min.ts +++ b/src/min.ts @@ -1,19 +1,21 @@ import Sequence from "./Sequence"; -/** - * Returns the minimum element of the sequence or `null` if sequence is empty. - * - * @returns {T} - */ -function min(this: Sequence): T | null { - let result: T | null = null; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (result == null || item < result) { - result = item; +export class Min { + + /** + * Returns the minimum element of the sequence or `null` if sequence is empty. + * + * @returns {T} + */ + min(this: Sequence): T | null { + let result: T | null = null; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (result == null || item < result) { + result = item; + } } + return result; } - return result; -} -export default min; \ No newline at end of file +} \ No newline at end of file diff --git a/src/minBy.ts b/src/minBy.ts index b495726..921ac09 100644 --- a/src/minBy.ts +++ b/src/minBy.ts @@ -1,24 +1,26 @@ import Sequence from "./Sequence"; -/** - * Returns the minimum element by comparing the results of the given `selector` function - * for each element of the sequence or `null` if the sequence is empty. - * - * @param {(value: T) => R} selector - * @returns {T} - */ -function minBy(this: Sequence, selector: (value: T) => R): T | null { - let min: T | null = null; - let minSelected: R | null = null; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - const value = selector(item); - if (minSelected == null || value < minSelected) { - minSelected = value; - min = item; +export class MinBy { + + /** + * Returns the minimum element by comparing the results of the given `selector` function + * for each element of the sequence or `null` if the sequence is empty. + * + * @param {(value: T) => R} selector + * @returns {T} + */ + minBy(this: Sequence, selector: (value: T) => R): T | null { + let min: T | null = null; + let minSelected: R | null = null; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + const value = selector(item); + if (minSelected == null || value < minSelected) { + minSelected = value; + min = item; + } } + return min; } - return min; -} -export default minBy; \ No newline at end of file +} \ No newline at end of file diff --git a/src/minWith.ts b/src/minWith.ts index 5e9fe10..d554188 100644 --- a/src/minWith.ts +++ b/src/minWith.ts @@ -1,20 +1,22 @@ import Sequence from "./Sequence"; -/** - * Returns the minimum element of the sequence by evaluating the given `compare` - * function or `null` if sequence is empty. - * - * @returns {T} - */ -function minWith(this: Sequence, compare: (a: T, b: T) => number): T | null { - let min: T | null = null; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (min == null || compare(item, min) < 0) { - min = item; +export class MinWith { + + /** + * Returns the minimum element of the sequence by evaluating the given `compare` + * function or `null` if sequence is empty. + * + * @returns {T} + */ + minWith(this: Sequence, compare: (a: T, b: T) => number): T | null { + let min: T | null = null; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (min == null || compare(item, min) < 0) { + min = item; + } } + return min; } - return min; -} -export default minWith; \ No newline at end of file +} \ No newline at end of file diff --git a/src/minus.ts b/src/minus.ts index 4a985de..9fefebe 100644 --- a/src/minus.ts +++ b/src/minus.ts @@ -1,21 +1,23 @@ -import Sequence from "./Sequence"; +import Sequence, {isSequence} from "./Sequence"; -/** - * Removes the given `data` and returns a new sequence. Data can either be a single element, an array of elements - * or a sequence of elements. - * - * @param {Sequence | Array | T} data - * @returns {Sequence} - */ -function minus(this: Sequence, data: T | Sequence | Array): Sequence { - if (data instanceof Sequence) { - const array: Array = data.toArray(); - return this.filter(it => array.indexOf(it) < 0); - } else if (data instanceof Array) { - return this.filter(it => data.indexOf(it) < 0); - } else { - return this.filter(it => it !== data); +export class Minus { + + /** + * Removes the given `data` and returns a new sequence. Data can either be a single element, an array of elements + * or a sequence of elements. + * + * @param {Sequence | Array | T} data + * @returns {Sequence} + */ + minus(this: Sequence, data: T | Sequence | Array): Sequence { + if (isSequence(data)) { + const array: Array = data.toArray(); + return this.filter(it => array.indexOf(it) < 0); + } else if (data instanceof Array) { + return this.filter(it => data.indexOf(it) < 0); + } else { + return this.filter(it => it !== data); + } } -} -export default minus; \ No newline at end of file +} \ No newline at end of file diff --git a/src/none.ts b/src/none.ts index 7cbe22e..1abf924 100644 --- a/src/none.ts +++ b/src/none.ts @@ -1,23 +1,25 @@ import Sequence from "./Sequence"; -/** - * Returns `true` if no element match the given `predicate` or if the sequence is empty - * if no predicate is present. - * - * @param {(value: T) => boolean} predicate - * @returns {boolean} - */ -function none(this: Sequence, predicate?: (value: T) => boolean): boolean { - if (predicate == null) { - return !this.iterator.hasNext(); - } - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (predicate(item)) { - return false; +export class None { + + /** + * Returns `true` if no element match the given `predicate` or if the sequence is empty + * if no predicate is present. + * + * @param {(value: T) => boolean} predicate + * @returns {boolean} + */ + none(this: Sequence, predicate?: (value: T) => boolean): boolean { + if (predicate == null) { + return !this.iterator.hasNext(); + } + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (predicate(item)) { + return false; + } } + return true; } - return true; -} -export default none; \ No newline at end of file +} \ No newline at end of file diff --git a/src/onEach.ts b/src/onEach.ts index c9eaee8..3fc3645 100644 --- a/src/onEach.ts +++ b/src/onEach.ts @@ -1,16 +1,18 @@ import Sequence from "./Sequence"; -/** - * Performs the given `action` for each element and returns the sequence. - * - * @param {(value: T) => void} action - * @returns {Sequence} - */ -function onEach(this: Sequence, action: (value: T) => void): Sequence { - return this.map(it => { - action(it); - return it; - }); -} +export class OnEach { -export default onEach; \ No newline at end of file + /** + * Performs the given `action` for each element and returns the sequence. + * + * @param {(value: T) => void} action + * @returns {Sequence} + */ + onEach(this: Sequence, action: (value: T) => void): Sequence { + return this.map(it => { + action(it); + return it; + }); + } + +} \ No newline at end of file diff --git a/src/partition.ts b/src/partition.ts index 8f38835..72f42bc 100644 --- a/src/partition.ts +++ b/src/partition.ts @@ -1,24 +1,26 @@ import Sequence from "./Sequence"; -/** - * Evaluates the given `predicate` for each element of the sequence and assorts each element into one of two lists - * according to the result of the predicate. Returns both lists as an object. - * - * @param {(value: T) => boolean} predicate - * @returns {{true: Array; false: Array}} - */ -function partition(this: Sequence, predicate: (value: T) => boolean): { "true": Array, "false": Array } { - const arrayTrue: Array = []; - const arrayFalse: Array = []; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - if (predicate(item)) { - arrayTrue.push(item); - } else { - arrayFalse.push(item); +export class Partition { + + /** + * Evaluates the given `predicate` for each element of the sequence and assorts each element into one of two lists + * according to the result of the predicate. Returns both lists as an object. + * + * @param {(value: T) => boolean} predicate + * @returns {{true: Array; false: Array}} + */ + partition(this: Sequence, predicate: (value: T) => boolean): { "true": Array, "false": Array } { + const arrayTrue: Array = []; + const arrayFalse: Array = []; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + if (predicate(item)) { + arrayTrue.push(item); + } else { + arrayFalse.push(item); + } } + return { "true": arrayTrue, "false": arrayFalse }; } - return {"true": arrayTrue, "false": arrayFalse}; -} -export default partition; \ No newline at end of file +} \ No newline at end of file diff --git a/src/plus.ts b/src/plus.ts index 1306baa..372f53f 100644 --- a/src/plus.ts +++ b/src/plus.ts @@ -1,11 +1,10 @@ -import Sequence from "./Sequence"; +import Sequence, {createSequence, isSequence} from "./Sequence"; import SequenceIterator, {IterableIterator} from "./SequenceIterator"; class AppendIterator implements SequenceIterator { - constructor( - private readonly first: SequenceIterator, - private readonly second: SequenceIterator - ) {} + constructor(private readonly first: SequenceIterator, + private readonly second: SequenceIterator) { + } hasNext(): boolean { return this.first.hasNext() || this.second.hasNext(); @@ -19,21 +18,40 @@ class AppendIterator implements SequenceIterator { } -/** - * Appends the given `data` to the end of the sequence and returns a new sequence. Data can either be a single element, - * an array of elements or a sequence of elements. - * - * @param {Sequence | Array | T} data - * @returns {Sequence} - */ -function plus(this: Sequence, data: T | Sequence | Array): Sequence { - if (data instanceof Sequence) { - return new Sequence(new AppendIterator(this.iterator, data.iterator)); - } else if (data instanceof Array) { - return new Sequence(new AppendIterator(this.iterator, new IterableIterator(data))); - } else { - return new Sequence(new AppendIterator(this.iterator, new IterableIterator([data]))); +export class Plus { + + /** + * Appends the given `element` to the end of the sequence and returns a new sequence. + * + * @param {T} element + * @returns {Sequence} + */ + plus(this: Sequence, element: T): Sequence; + + /** + * Appends the given array to the end of the sequence and returns a new sequence. + * + * @param {Array} other + * @returns {Sequence} + */ + plus(this: Sequence, other: Array): Sequence; + + /** + * Appends the given sequence to the end of the sequence and returns a new sequence. + * + * @param {Sequence} other + * @returns {Sequence} + */ + plus(this: Sequence, other: Sequence): Sequence; + + plus(this: Sequence, data: T | Sequence | Array): Sequence { + if (isSequence(data)) { + return createSequence(new AppendIterator(this.iterator, data.iterator)); + } else if (data instanceof Array) { + return createSequence(new AppendIterator(this.iterator, new IterableIterator(data))); + } else { + return createSequence(new AppendIterator(this.iterator, new IterableIterator([data]))); + } } -} -export default plus; \ No newline at end of file +} \ No newline at end of file diff --git a/src/reduce.ts b/src/reduce.ts index 957389a..7798610 100644 --- a/src/reduce.ts +++ b/src/reduce.ts @@ -1,24 +1,26 @@ import Sequence from "./Sequence"; -/** - * Reduces the whole sequence to a single value by invoking `operation` with each element - * from left to right. For every invocation of the operation `acc` is the result of the last - * invocation. For the first invocation of the operation `acc` is the first element of the - * sequence. - * - * @param {(acc: S, value: T) => S} operation - * @returns {S} - */ -function reduce(this: Sequence, operation: (acc: S, value: T) => S): S { - if (!this.iterator.hasNext()) { - throw new Error("Cannot reduce empty sequence"); - } - let result: S = this.iterator.next(); - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - result = operation(result, item); +export class Reduce { + + /** + * Reduces the whole sequence to a single value by invoking `operation` with each element + * from left to right. For every invocation of the operation `acc` is the result of the last + * invocation. For the first invocation of the operation `acc` is the first element of the + * sequence. + * + * @param {(acc: S, value: T) => S} operation + * @returns {S} + */ + reduce(this: Sequence, operation: (acc: S, value: T) => S): S { + if (!this.iterator.hasNext()) { + throw new Error("Cannot reduce empty sequence"); + } + let result: S = this.iterator.next(); + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + result = operation(result, item); + } + return result; } - return result; -} -export default reduce; \ No newline at end of file +} \ No newline at end of file diff --git a/src/reduceIndexed.ts b/src/reduceIndexed.ts index 12e820b..3f14094 100644 --- a/src/reduceIndexed.ts +++ b/src/reduceIndexed.ts @@ -1,26 +1,28 @@ import Sequence from "./Sequence"; -/** - * Reduces the whole sequence to a single value by invoking `operation` with each element - * from left to right. For every invocation of the operation `acc` is the result of the last - * invocation. For the first invocation of the operation `acc` is the first element of the - * sequence. In addition the `index` of the current element is also passed to the operation. - * - * @param {(index: number, acc: S, element: T) => S} operation - * @returns {S} - */ -function reduceIndexed(this: Sequence, operation: (index: number, acc: S, element: T) => S): S { - if (!this.iterator.hasNext()) { - throw new Error("Cannot reduce empty sequence"); - } - let index = 1; - let result: S = this.iterator.next(); - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - result = operation(index, result, item); - index++; +export class ReduceIndexed { + + /** + * Reduces the whole sequence to a single value by invoking `operation` with each element + * from left to right. For every invocation of the operation `acc` is the result of the last + * invocation. For the first invocation of the operation `acc` is the first element of the + * sequence. In addition the `index` of the current element is also passed to the operation. + * + * @param {(index: number, acc: S, element: T) => S} operation + * @returns {S} + */ + reduceIndexed(this: Sequence, operation: (index: number, acc: S, element: T) => S): S { + if (!this.iterator.hasNext()) { + throw new Error("Cannot reduce empty sequence"); + } + let index = 1; + let result: S = this.iterator.next(); + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + result = operation(index, result, item); + index++; + } + return result; } - return result; -} -export default reduceIndexed; \ No newline at end of file +} \ No newline at end of file diff --git a/src/reverse.ts b/src/reverse.ts index 031c909..d2633cf 100644 --- a/src/reverse.ts +++ b/src/reverse.ts @@ -1,14 +1,16 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence with all elements of the sequence in reverse order. - * - * @returns {Sequence} - */ -function reverse(this: Sequence): Sequence { - return this.withIndex() - .sortedByDescending(it => it.index) - .map(it => it.value); -} +export class Reverse { -export default reverse; \ No newline at end of file + /** + * Returns a new sequence with all elements of the sequence in reverse order. + * + * @returns {Sequence} + */ + reverse(this: Sequence): Sequence { + return this.withIndex() + .sortedByDescending(it => it.index) + .map(it => it.value); + } + +} \ No newline at end of file diff --git a/src/single.ts b/src/single.ts index 5c25fe8..f1d7a81 100644 --- a/src/single.ts +++ b/src/single.ts @@ -1,25 +1,27 @@ import Sequence from "./Sequence"; -/** - * Returns the single element of the sequence or throws error if the sequence has more than - * one element or none at all. If a `predicate` is passed returns the single element matching - * the predicate or throws an error if more or less than one element match the predicate. - * - * @param {(value: T) => boolean} predicate - * @returns {T} - */ -function single(this: Sequence, predicate?: (value: T) => boolean): T { - if (predicate != null) { - return this.filter(predicate).single(); - } - if (!this.iterator.hasNext()) { - throw new Error("No such element"); - } - const item = this.iterator.next(); - if (this.iterator.hasNext()) { - throw new Error("Expect single element"); +export class Single { + + /** + * Returns the single element of the sequence or throws error if the sequence has more than + * one element or none at all. If a `predicate` is passed returns the single element matching + * the predicate or throws an error if more or less than one element match the predicate. + * + * @param {(value: T) => boolean} predicate + * @returns {T} + */ + single(this: Sequence, predicate?: (value: T) => boolean): T { + if (predicate != null) { + return this.filter(predicate).single(); + } + if (!this.iterator.hasNext()) { + throw new Error("No such element"); + } + const item = this.iterator.next(); + if (this.iterator.hasNext()) { + throw new Error("Expect single element"); + } + return item; } - return item; -} -export default single; \ No newline at end of file +} \ No newline at end of file diff --git a/src/singleOrNull.ts b/src/singleOrNull.ts index df27ef0..8304895 100644 --- a/src/singleOrNull.ts +++ b/src/singleOrNull.ts @@ -1,25 +1,27 @@ import Sequence from "./Sequence"; -/** - * Returns the single element of the sequence or `null` if the sequence has more than - * one element or none at all. If a `predicate` is passed returns the single element matching - * the predicate or `null` if more or less than one element match the predicate. - * - * @param {(value: T) => boolean} predicate - * @returns {T} - */ -function singleOrNull(this: Sequence, predicate?: (value: T) => boolean): T | null { - if (predicate != null) { - return this.filter(predicate).singleOrNull(); - } - if (!this.iterator.hasNext()) { - return null; - } - const item = this.iterator.next(); - if (this.iterator.hasNext()) { - return null; +export class SingleOrNull { + + /** + * Returns the single element of the sequence or `null` if the sequence has more than + * one element or none at all. If a `predicate` is passed returns the single element matching + * the predicate or `null` if more or less than one element match the predicate. + * + * @param {(value: T) => boolean} predicate + * @returns {T} + */ + singleOrNull(this: Sequence, predicate?: (value: T) => boolean): T | null { + if (predicate != null) { + return this.filter(predicate).singleOrNull(); + } + if (!this.iterator.hasNext()) { + return null; + } + const item = this.iterator.next(); + if (this.iterator.hasNext()) { + return null; + } + return item; } - return item; -} -export default singleOrNull; \ No newline at end of file +} \ No newline at end of file diff --git a/src/sorted.ts b/src/sorted.ts index f86a60c..1670232 100644 --- a/src/sorted.ts +++ b/src/sorted.ts @@ -1,29 +1,31 @@ -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; import {IterableIterator} from "./SequenceIterator"; import ComparatorFactory from "./ComparatorFactory"; import Comparator from "./Comparator"; import createComparatorFactory from "./createComparatorFactory"; -/** - * Returns a new sequence with all elements sorted by the comparator specified by the given `composeComparator` function - * or in natural order if no arguments are given. - * - * @returns {Sequence} - */ -function sorted(this: Sequence, composeComparator?: (factory: ComparatorFactory) => Comparator): Sequence { - const result: Array = []; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - result.push(item); - } - if (composeComparator == null) { - result.sort(); - } else { - const factory: ComparatorFactory = createComparatorFactory(); - const comparator = composeComparator(factory); - result.sort(comparator); +export class Sorted { + + /** + * Returns a new sequence with all elements sorted by the comparator specified by the given `composeComparator` function + * or in natural order if no arguments are given. + * + * @returns {Sequence} + */ + sorted(this: Sequence, composeComparator?: (factory: ComparatorFactory) => Comparator): Sequence { + const result: Array = []; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + result.push(item); + } + if (composeComparator == null) { + result.sort(); + } else { + const factory: ComparatorFactory = createComparatorFactory(); + const comparator = composeComparator(factory); + result.sort(comparator); + } + return createSequence(new IterableIterator(result)); } - return new Sequence(new IterableIterator(result)); -} -export default sorted; \ No newline at end of file +} \ No newline at end of file diff --git a/src/sortedBy.ts b/src/sortedBy.ts index d63be23..26412a2 100644 --- a/src/sortedBy.ts +++ b/src/sortedBy.ts @@ -1,14 +1,16 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence with all elements sorted ascending by the value specified - * by the given `selector` function. - * - * @param {(value: T) => R} selector - * @returns {Sequence} - */ -function sortedBy(this: Sequence, selector: (value: T) => R): Sequence { - return this.sorted(it => it.compareBy(selector)); -} +export class SortedBy { -export default sortedBy; \ No newline at end of file + /** + * Returns a new sequence with all elements sorted ascending by the value specified + * by the given `selector` function. + * + * @param {(value: T) => R} selector + * @returns {Sequence} + */ + sortedBy(this: Sequence, selector: (value: T) => R): Sequence { + return this.sorted(it => it.compareBy(selector)); + } + +} \ No newline at end of file diff --git a/src/sortedByDescending.ts b/src/sortedByDescending.ts index f3f7091..1a03761 100644 --- a/src/sortedByDescending.ts +++ b/src/sortedByDescending.ts @@ -1,14 +1,16 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence with all elements sorted descending by the value specified - * by the given `selector` function. - * - * @param {(value: T) => R} selector - * @returns {Sequence} - */ -function sortedByDescending(this: Sequence, selector: (value: T) => R): Sequence { - return this.sorted(it => it.compareByDescending(selector)); -} +export class SortedByDescending { -export default sortedByDescending; \ No newline at end of file + /** + * Returns a new sequence with all elements sorted descending by the value specified + * by the given `selector` function. + * + * @param {(value: T) => R} selector + * @returns {Sequence} + */ + sortedByDescending(this: Sequence, selector: (value: T) => R): Sequence { + return this.sorted(it => it.compareByDescending(selector)); + } + +} \ No newline at end of file diff --git a/src/sortedDescending.ts b/src/sortedDescending.ts index b27f084..104d22f 100644 --- a/src/sortedDescending.ts +++ b/src/sortedDescending.ts @@ -1,12 +1,14 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence with all elements sorted in reverse (descending) natural order. - * - * @returns {Sequence} - */ -function sortedDescending(this: Sequence): Sequence { - return this.sorted(it => it.reverseOrder()); -} +export class SortedDescending { -export default sortedDescending; \ No newline at end of file + /** + * Returns a new sequence with all elements sorted in reverse (descending) natural order. + * + * @returns {Sequence} + */ + sortedDescending(this: Sequence): Sequence { + return this.sorted(it => it.reverseOrder()); + } + +} \ No newline at end of file diff --git a/src/sortedWith.ts b/src/sortedWith.ts index 0e85a31..34a74a6 100644 --- a/src/sortedWith.ts +++ b/src/sortedWith.ts @@ -1,14 +1,15 @@ import Sequence from "./Sequence"; -import {IterableIterator} from "./SequenceIterator"; -/** - * Returns a new sequence with all elements sorted be the given `compare` function. - * - * @param {(a: T, b: T) => number} comparison - * @returns {Sequence} - */ -function sortedWith(this: Sequence, comparison: (a: T, b: T) => number): Sequence { - return this.sorted(it => it.compare(comparison)); -} +export class SortedWith { -export default sortedWith; \ No newline at end of file + /** + * Returns a new sequence with all elements sorted be the given `compare` function. + * + * @param {(a: T, b: T) => number} comparison + * @returns {Sequence} + */ + sortedWith(this: Sequence, comparison: (a: T, b: T) => number): Sequence { + return this.sorted(it => it.compare(comparison)); + } + +} \ No newline at end of file diff --git a/src/sum.ts b/src/sum.ts index 11c444b..2508053 100644 --- a/src/sum.ts +++ b/src/sum.ts @@ -1,16 +1,18 @@ import Sequence from "./Sequence"; -/** - * Returns the sum of all numbers. - * - * @returns {number} - */ -function sum(this: Sequence): number { - let result = 0; - while (this.iterator.hasNext()) { - result += this.iterator.next(); +export class Sum { + + /** + * Returns the sum of all numbers. + * + * @returns {number} + */ + sum(this: Sequence): number { + let result = 0; + while (this.iterator.hasNext()) { + result += this.iterator.next(); + } + return result; } - return result; -} -export default sum; \ No newline at end of file +} \ No newline at end of file diff --git a/src/sumBy.ts b/src/sumBy.ts index 6fdb2d4..ea2184d 100644 --- a/src/sumBy.ts +++ b/src/sumBy.ts @@ -1,18 +1,20 @@ import Sequence from "./Sequence"; -/** - * Returns the sum of all numbers specified by the given `selector` function. - * - * @param {(value: T) => number} selector - * @returns {number} - */ -function sumBy(this: Sequence, selector: (value: T) => number): number { - let result = 0; - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - result += selector(item); +export class SumBy { + + /** + * Returns the sum of all numbers specified by the given `selector` function. + * + * @param {(value: T) => number} selector + * @returns {number} + */ + sumBy(this: Sequence, selector: (value: T) => number): number { + let result = 0; + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + result += selector(item); + } + return result; } - return result; -} -export default sumBy; \ No newline at end of file +} \ No newline at end of file diff --git a/src/take.ts b/src/take.ts index e734545..06fc68c 100644 --- a/src/take.ts +++ b/src/take.ts @@ -1,14 +1,18 @@ import Sequence from "./Sequence"; -/** - * Returns a new sequence consisting of the first `n` elements. All other elements - * are discarded. - * - * @param {number} n - * @returns {Sequence} - */ -export default function take(this: Sequence, n: number): Sequence { - return this.withIndex() - .takeWhile(it => it.index < n) - .map(it => it.value); +export class Take { + + /** + * Returns a new sequence consisting of the first `n` elements. All other elements + * are discarded. + * + * @param {number} n + * @returns {Sequence} + */ + take(this: Sequence, n: number): Sequence { + return this.withIndex() + .takeWhile(it => it.index < n) + .map(it => it.value); + } + } \ No newline at end of file diff --git a/src/takeWhile.ts b/src/takeWhile.ts index 3a870d6..c86ba38 100644 --- a/src/takeWhile.ts +++ b/src/takeWhile.ts @@ -1,4 +1,4 @@ -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; import SequenceIterator from "./SequenceIterator"; class TakeWhileIterator implements SequenceIterator { @@ -38,14 +38,16 @@ class TakeWhileIterator implements SequenceIterator { } } -/** - * Takes all elements of the sequence as long as the given `predicate` evaluates to true. - * - * @param {(item: T) => boolean} predicate - * @returns {Sequence} - */ -function takeWhile(this: Sequence, predicate: (item: T) => boolean): Sequence { - return new Sequence(new TakeWhileIterator(this.iterator, predicate)); -} +export class TakeWhile { + + /** + * Takes all elements of the sequence as long as the given `predicate` evaluates to true. + * + * @param {(item: T) => boolean} predicate + * @returns {Sequence} + */ + takeWhile(this: Sequence, predicate: (item: T) => boolean): Sequence { + return createSequence(new TakeWhileIterator(this.iterator, predicate)); + } -export default takeWhile; \ No newline at end of file +} \ No newline at end of file diff --git a/src/toArray.ts b/src/toArray.ts index 9a59610..c2daab6 100644 --- a/src/toArray.ts +++ b/src/toArray.ts @@ -1,18 +1,31 @@ import Sequence from "./Sequence"; -/** - * Returns all elements of the sequence as array. If an `array` is passed - * the elements are appended to the end of the array. - * - * @param {Array} array - * @returns {Array} - */ -function toArray(this: Sequence, array?: Array): Array { - const result: Array = array || []; - while (this.iterator.hasNext()) { - result.push(this.iterator.next()); +export class ToArray { + + /** + * Returns all elements of the sequence as array. If an `array` is passed + * the elements are appended to the end of the array. + * + * @param {Array} array + * @returns {Array} + */ + toArray(this: Sequence, array?: Array): Array { + const result: Array = array || []; + while (this.iterator.hasNext()) { + result.push(this.iterator.next()); + } + return result; + } + + /** + * Returns all elements of the sequence as array. If an `array` is passed + * the elements are appended to the end of the array. + * + * @param {Array} array + * @returns {Array} + */ + toList(this: Sequence, array?: Array): Array { + return this.toArray(array); } - return result; -} -export default toArray; \ No newline at end of file +} \ No newline at end of file diff --git a/src/toMap.ts b/src/toMap.ts index c3d4b79..0129291 100644 --- a/src/toMap.ts +++ b/src/toMap.ts @@ -1,21 +1,23 @@ import Sequence from "./Sequence"; -/** - * Returns a map consisting of each key-value pair. If a `map` is passed - * the pairs are set on this map. Duplicate keys override each other. - * - * @param {Map} map - * @returns {Map} - */ -function toMap(this: Sequence<[K, V]>, map?: Map): Map { - const result = map || new Map(); - while (this.iterator.hasNext()) { - const pair = this.iterator.next(); - const key = pair[0]; - const value = pair[1]; - result.set(key, value); +export class ToMap { + + /** + * Returns a map consisting of each key-value pair. If a `map` is passed + * the pairs are set on this map. Duplicate keys override each other. + * + * @param {Map} map + * @returns {Map} + */ + toMap(this: Sequence<[K, V]>, map?: Map): Map { + const result = map || new Map(); + while (this.iterator.hasNext()) { + const pair = this.iterator.next(); + const key = pair[0]; + const value = pair[1]; + result.set(key, value); + } + return result; } - return result; -} -export default toMap; \ No newline at end of file +} \ No newline at end of file diff --git a/src/toSet.ts b/src/toSet.ts index 423772a..328952b 100644 --- a/src/toSet.ts +++ b/src/toSet.ts @@ -1,19 +1,21 @@ import Sequence from "./Sequence"; -/** - * Returns all elements of the sequence as set. If a `set` is passed - * the elements are added to this set. - * - * @param {Set} set - * @returns {Set} - */ -function toSet(this: Sequence, set?: Set): Set { - const result = set || new Set(); - while (this.iterator.hasNext()) { - const item = this.iterator.next(); - result.add(item); +export class ToSet { + + /** + * Returns all elements of the sequence as set. If a `set` is passed + * the elements are added to this set. + * + * @param {Set} set + * @returns {Set} + */ + toSet(this: Sequence, set?: Set): Set { + const result = set || new Set(); + while (this.iterator.hasNext()) { + const item = this.iterator.next(); + result.add(item); + } + return result; } - return result; -} -export default toSet; \ No newline at end of file +} \ No newline at end of file diff --git a/src/unzip.ts b/src/unzip.ts index 76bef50..72a62ee 100644 --- a/src/unzip.ts +++ b/src/unzip.ts @@ -1,20 +1,22 @@ import Sequence from "./Sequence"; -/** - * Returns a pair of arrays where the first array contains all first values - * and the second array all second values from each input pair of the sequence. - * - * @returns {[Array , Array]} - */ -function unzip(this: Sequence<[T, S]>): [Array, Array] { - const array1: Array = []; - const array2: Array = []; - while (this.iterator.hasNext()) { - const [first, second] = this.iterator.next(); - array1.push(first); - array2.push(second); +export class Unzip { + + /** + * Returns a pair of arrays where the first array contains all first values + * and the second array all second values from each input pair of the sequence. + * + * @returns {[Array , Array]} + */ + unzip(this: Sequence<[T, S]>): [Array, Array] { + const array1: Array = []; + const array2: Array = []; + while (this.iterator.hasNext()) { + const [first, second] = this.iterator.next(); + array1.push(first); + array2.push(second); + } + return [array1, array2]; } - return [array1, array2]; -} -export default unzip; \ No newline at end of file +} \ No newline at end of file diff --git a/src/withIndex.ts b/src/withIndex.ts index 06d93cf..ad213fa 100644 --- a/src/withIndex.ts +++ b/src/withIndex.ts @@ -1,4 +1,4 @@ -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; import IndexedValue from "./IndexedValue"; import SequenceIterator from "./SequenceIterator"; @@ -18,13 +18,15 @@ class IndexIterator implements SequenceIterator> { } } -/** - * Returns a new sequence consisting of indexed values for all original elements. - * - * @returns {Sequence>} - */ -function withIndex(this: Sequence): Sequence> { - return new Sequence(new IndexIterator(this.iterator)); -} +export class WithIndex { + + /** + * Returns a new sequence consisting of indexed values for all original elements. + * + * @returns {Sequence>} + */ + withIndex(this: Sequence): Sequence> { + return createSequence(new IndexIterator(this.iterator)); + } -export default withIndex; \ No newline at end of file +} \ No newline at end of file diff --git a/src/zip.ts b/src/zip.ts index 7f1cb5f..1ff8d99 100644 --- a/src/zip.ts +++ b/src/zip.ts @@ -1,4 +1,4 @@ -import Sequence from "./Sequence"; +import Sequence, {createSequence} from "./Sequence"; import SequenceIterator from "./SequenceIterator"; class ZipIterator implements SequenceIterator<[T, S]> { @@ -19,16 +19,18 @@ class ZipIterator implements SequenceIterator<[T, S]> { } -/** - * Returns a new sequence consisting of pairs built the elements of both sequences - * with the same index. The resulting sequence has the length of the shortest input - * sequence. All other elements are discarded. - * - * @param {Sequence} other - * @returns {Sequence<[T , S]>} - */ -function zip(this: Sequence, other: Sequence): Sequence<[T, S]> { - return new Sequence(new ZipIterator(this.iterator, other.iterator)); -} +export class Zip { + + /** + * Returns a new sequence consisting of pairs built the elements of both sequences + * with the same index. The resulting sequence has the length of the shortest input + * sequence. All other elements are discarded. + * + * @param {Sequence} other + * @returns {Sequence<[T , S]>} + */ + zip(this: Sequence, other: Sequence): Sequence<[T, S]> { + return createSequence(new ZipIterator(this.iterator, other.iterator)); + } -export default zip; \ No newline at end of file +} \ No newline at end of file diff --git a/test/associateBy.test.ts b/test/associateBy.test.ts index 58cad05..ec2c6bf 100644 --- a/test/associateBy.test.ts +++ b/test/associateBy.test.ts @@ -15,6 +15,20 @@ describe("associateBy", () => { expect(map.get(3)).toBe(c); }); + it("should associate map by key", () => { + const a = {k: 1, v: 11}; + const b = {k: 2, v: 22}; + const c = {k: 3, v: 33}; + + const map = sequenceOf(a, b, c) + .associateBy("k"); + + expect(map.size).toBe(3); + expect(map.get(1)).toBe(a); + expect(map.get(2)).toBe(b); + expect(map.get(3)).toBe(c); + }); + it("should associate map by keySelector and valueTransformer", () => { const a = {k: 1, v: 11}; const b = {k: 2, v: 22}; @@ -32,6 +46,23 @@ describe("associateBy", () => { expect(map.get(3)).toBe(33); }); + it("should associate map by key and valueTransformer", () => { + const a = {k: 1, v: 11}; + const b = {k: 2, v: 22}; + const c = {k: 3, v: 33}; + + const map = sequenceOf(a, b, c) + .associateBy( + "k", + it => it.v + ); + + expect(map.size).toBe(3); + expect(map.get(1)).toBe(11); + expect(map.get(2)).toBe(22); + expect(map.get(3)).toBe(33); + }); + it("latest entries should win in case of duplicates", () => { const a = {k: 1, v: 11}; const b = {k: 2, v: 22}; diff --git a/test/extendSequence.test.ts b/test/extendSequence.test.ts new file mode 100644 index 0000000..0c8cdd4 --- /dev/null +++ b/test/extendSequence.test.ts @@ -0,0 +1,22 @@ +import Sequence, {extendSequence, sequenceOf} from "../src/Sequence"; + +class GreetAll { + greetAll(this: Sequence): string { + const names = this.joinToString({ separator: ", " }); + return "Hello " + names + " !"; + } +} + +declare module "../src/Sequence" { + export default interface Sequence extends GreetAll { + } +} + +describe("extendSequence", () => { + it("should extend Sequence implementation prototype with provided mixin", () => { + extendSequence(GreetAll); + const names = sequenceOf("John", "Bob", "Steve"); + const greetings = names.greetAll(); + expect(greetings).toBe("Hello John, Bob, Steve !"); + }); +});