From d1cf5cc4b5f0dae3d5f1bad33ea9ed4ddcc65fc5 Mon Sep 17 00:00:00 2001 From: Ventsislav Date: Fri, 2 Aug 2019 09:44:07 +0200 Subject: [PATCH] 1. Add fromArrayLike, 2. Reformat and clean the code --- .editorconfig | 7 + .gitignore | 7 +- index.d.ts | 340 +++++++++---------- index.esm.js | 2 +- index.js | 603 ++++++++++++++++++++++------------ src/creation.js | 39 +++ src/finalizers/first.js | 6 +- src/finalizers/single.js | 6 +- src/generators/range.js | 36 +- src/index.js | 29 +- src/iterables/distinct.js | 30 +- src/iterables/group.js | 6 +- src/iterables/select-many.js | 30 +- src/iterables/select.js | 66 ++-- src/iterables/skip.js | 28 +- src/iterables/take.js | 46 +-- src/iterables/where.js | 74 ++--- src/linq-mixin.js | 12 +- src/utils.js | 13 +- test/benchmark/select.perf.js | 27 +- test/benchmark/take.perf.js | 26 +- test/unit/first.spec.js | 4 +- test/unit/from.spec.js | 24 +- test/unit/group.spec.js | 9 +- test/unit/of-type.spec.js | 11 +- test/unit/range.spec.js | 8 +- test/unit/select.spec.js | 6 +- test/unit/single.spec.js | 12 +- test/unit/skip.spec.js | 26 +- test/unit/take.spec.js | 26 +- test/unit/to.spec.js | 4 +- 31 files changed, 882 insertions(+), 681 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fd3e630 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true +[{*.js,*.ts}] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space + diff --git a/.gitignore b/.gitignore index 2f24c57..7eff499 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -node_modules/ -coverage/ -.nyc_output/ +node_modules/ +coverage/ +.nyc_output/ +.idea diff --git a/index.d.ts b/index.d.ts index 0fbe30e..284f252 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,193 +1,147 @@ -declare module 'modern-linq' { - export interface LinqIterable extends Iterable { - /** - * Filters the iterable using predicate function - * @param predicate - */ - where(predicate: (item: TValue) => boolean): LinqIterable; - /** - * Maps the iterable items - * @param map map function - */ - select(map: (item: TValue) => TOutput): LinqIterable; - /** - * Flat Iterable of collections - * @param map Function which returns a collection - */ - selectMany(map: (item: TValue) => TOutput[]): LinqIterable; - /** - * Take first N items from iterable - * @param count - */ - take(count: number): LinqIterable; - /** - * Skip first N items from iterable - * @param count - */ - skip(count: number): LinqIterable; - /** - * Return distinct items. Can specify optional item comparer - * @param count - */ - disticnt(comparer?: (a: TValue, b: TValue) => boolean): LinqIterable; - /** - * Selects all items of type string - * @param type - */ - ofType(type: 'string'): LinqIterable; - /** - * Selects all items of type number - * @param type - */ - ofType(type: 'number'): LinqIterable; - /** - * Selects all items of type boolean - * @param type - */ - ofType(type: 'boolean'): LinqIterable; - /** - * Selects all items of type undefined - * @param type - */ - ofType(type: 'undefined'): LinqIterable; - /** - * Selects all items of type function - * @param type - */ - ofType(type: 'function'): LinqIterable; - /** - * Selects all items of type object - * @param type - */ - ofType(type: 'object'): LinqIterable; - /** - * Selects all items of type symbol - * @param type - */ - ofType(type: 'symbol'): LinqIterable; - /** - * Selects all items of base type - * @param type - */ - ofType(type: { prototype: TOutput }): LinqIterable; - - /** - * Group a list by key - * @param keySelector function which extract the key from collection item - */ - groupBy(keySelector: (item: TValue) => TKey): IGrouping; - - /** - * Group a list by key - * @param keySelector function which extract the key from collection item - * @param elementSelector function which extract the element - */ - groupBy(keySelector: (item: TValue) => TKey, elementSelector: (item: TValue) => TElement): IGrouping; - - /** - * Group a list by key - * @param keySelector - * @param resultCreator - */ - groupBy(keySelector: (item: TValue) => TKey, resultCreator: (key: TKey, elements: LinqIterable) => TResult): LinqIterable; - - /** - * Group a list by key - * @param keySelector - * @param elementSelector - * @param resultCreator - */ - groupBy(keySelector: (item: TValue) => TKey, elementSelector: (item: TValue) => TElement, resultCreator: (key: TKey, elements: LinqIterable) => TResult): LinqIterable; - - /** - * Creates an array from iterable - */ - toArray(): TValue[]; - - /** - * Creates a map from iterable - */ - toMap(keySelector: (item: TValue) => TKey): Map; - - /** - * Creates a map from iterable - */ - toMap(keySelector: (item: TValue) => TKey, valueSelector: (item: TValue) => TElement): Map; - - /** - * Creates a set from iterable - */ - toSet(): Set; - - /** - * Get first item of iterable - */ - first(): TValue | undefined; - /** - * Get first item of iterable, if does not contain any return default - * @param def - */ - firstOrDefault(def: TValue): TValue; - /** - * Get first item of iterable, if no items throw RangeError - */ - firstOrThrow(): TValue | never; - /** - * Checks if iterable has only one item and returns it. - * If the iterable does not contain items or has multiple throws RangeError - */ - single(): TValue | never; - /** - * Checks if iterable has only one item and returns it. - * If the iterable does not contain items return default value. - * If contains multiple throws RangeError - */ - singleOrDefault(def: TValue): TValue | never; - - /** - * Returns if all items satisfy the predicate. It returns true if no items. - * @param predicate - */ - all(predicate: (item: TValue) => boolean): boolean; - - /** - * Returns if all items satisfy the predicate. It returns false if no items. - * @param predicate - */ - allAndEvery(predicate: (item: TValue) => boolean): boolean; - - /** - * Returns if any items satisfy the predicate. - * @param predicate - */ - any(predicate: (item: TValue) => boolean): boolean; - - /** - * Return number of items the sequence contains. - * @param predicate if specified, returns the count only on elements that satisfy it. - */ - count(predicate?: (item: TValue) => boolean): number; - } - - export interface IGrouping extends LinqIterable { - key: TKey; - } - - /** - * Creates a select js iterable from iterable (arrays, map, set ...) - * @param iterable - */ - export function fromIterable(iterable: Iterable): LinqIterable; - /** - * Creates a select js iterable from an object (using Object.entries()) - * @param value - */ - export function fromObject(value: TValue): LinqIterable<['string', object]>; - /** - * Creates a select js iterable containig a [from, to) range of numbers - * if from is less than to return ascending range - * if from is greater that to return descending range - * if from === to returns empty iterable - */ - export function range(from: number, to: number): LinqIterable; -} +declare module 'modern-linq' { + export interface LinqIterable extends Iterable { + /** + * Filters the iterable using predicate function + * @param predicate + */ + where(predicate: (item: TValue) => boolean): LinqIterable; + /** + * Maps the iterable items + * @param map map function + */ + select(map: (item: TValue) => TOutput): LinqIterable; + /** + * Flat Iterable of collections + * @param map Function which returns a collection + */ + selectMany(map: (item: TValue) => TOutput[]): LinqIterable; + /** + * Take first N items from iterable + * @param count + */ + take(count: number): LinqIterable; + /** + * Skip first N items from iterable + * @param count + */ + skip(count: number): LinqIterable; + /** + * Return distinct items. Can specify optional item comparer + * @param count + */ + disticnt(comparer?: (a: TValue, b: TValue) => boolean): LinqIterable; + /** + * Selects all items of type string + * @param type + */ + ofType(type: 'string'): LinqIterable; + /** + * Selects all items of type number + * @param type + */ + ofType(type: 'number'): LinqIterable; + /** + * Selects all items of type boolean + * @param type + */ + ofType(type: 'boolean'): LinqIterable; + /** + * Selects all items of type undefined + * @param type + */ + ofType(type: 'undefined'): LinqIterable; + /** + * Selects all items of type function + * @param type + */ + ofType(type: 'function'): LinqIterable; + /** + * Selects all items of type object + * @param type + */ + ofType(type: 'object'): LinqIterable; + /** + * Selects all items of type symbol + * @param type + */ + ofType(type: 'symbol'): LinqIterable; + /** + * Selects all items of base type + * @param type + */ + ofType(type: { prototype: TOutput }): LinqIterable; + + /** + * Creates an array from iterable + */ + toArray(): TValue[]; + + /** + * Get first item of iterable + */ + first(): TValue | undefined; + /** + * Get first item of iterable, if does not contain any return default + * @param def + */ + firstOrDefault(def: TValue): TValue; + /** + * Get first item of iterable, if no items throw RangeError + */ + firstOrThrow(): TValue | never; + /** + * Checks if iterable has only one item and returns it. + * If the iterable does not contain items or has multiple throws RangeError + */ + single(): TValue | never; + /** + * Checks if iterable has only one item and returns it. + * If the iterable does not contain items return default value. + * If contains multiple throws RangeError + */ + singleOrDefault(def: TValue): TValue | never; + + /** + * Returns if all items satisfy the predicate. It returns true if no items. + * @param predicate + */ + all(predicate: (item: TValue) => boolean): boolean; + + /** + * Returns if all items satisfy the predicate. It returns false if no items. + * @param predicate + */ + allAndEvery(predicate: (item: TValue) => boolean): boolean; + + /** + * Returns if any items satisfy the predicate. + * @param predicate + */ + any(predicate: (item: TValue) => boolean): boolean; + } + + /** + * Creates a select js iterable from iterable (arrays, map, set ...) + * @param iterable + */ + export function fromIterable(iterable: Iterable): LinqIterable; + /** + * Creates a select js iterable from an object (using Object.entries()) + * @param value + */ + export function fromObject(value: TValue): LinqIterable<['string', object]>; + + /** + * Creates linq iterable from array like object + * @param arrayLike + */ + export function fromArrayLike(arrayLike: ArrayLike): LinqIterable; + + /** + * Creates a select js iterable containig a [from, to) range of numbers + * if from is less than to return ascending range + * if from is greater that to return descending range + * if from === to returns empty iterable + */ + export function range(from: number, to: number): LinqIterable; +} diff --git a/index.esm.js b/index.esm.js index b804119..b928997 100644 --- a/index.esm.js +++ b/index.esm.js @@ -1 +1 @@ -export { fromIterable, fromObject, range } from './src/index'; +export { fromIterable, fromObject, fromArrayLike, range } from './src/index'; diff --git a/index.js b/index.js index 42117e9..01ee8c7 100644 --- a/index.js +++ b/index.js @@ -4,15 +4,12 @@ Object.defineProperty(exports, '__esModule', { value: true }); /** * Apply mixin to a class - * @param {Function} destination - * @param {Function} mixin + * @param {object} mixin + * @param {Function[]} destinations */ -function applyMixin(destination, mixin) { - for (const prop in mixin) { - if (prop === 'constructor' || !mixin.hasOwnProperty(prop)) { - continue; - } - destination.prototype[prop] = mixin[prop]; +function applyMixin(mixin, destinations) { + for (const dest of destinations) { + Object.assign(dest.prototype, mixin); } } @@ -20,67 +17,86 @@ function applyMixin(destination, mixin) { * Generates range of numbers [from, to) */ class RangeIterable { - /** - * The range is [from, to) - * @param {number} from - * @param {number} to - */ + /** + * The range is [from, to) + * @param {number} from + * @param {number} to + */ constructor(from, to) { this.from = from; this.to = to; } - + __ascendingRange() { const to = this.to; let current = this.from; - return { - next() { + return { + next() { if (current < to) { return { done: false, value: current++ }; } else { return { done: true }; } - } - }; + } + }; } __descendingRange() { const to = this.to; let current = this.from; - return { - next() { + return { + next() { if (current > to) { return { done: false, value: current-- }; } else { return { done: true }; } - } - }; + } + }; } - [Symbol.iterator]() { + [Symbol.iterator]() { if (this.from < this.to) { return this.__ascendingRange(); } if (this.from > this.to) { return this.__descendingRange(); } - return { next() { return { done: true } } }; + return { + next() { + return { done: true } + } + }; } } -class BaseLinqIterable { - constructor() { - this.isResulted = false; - this.result = null; +class BaseLinqIterable { + constructor() { + this.isResulted = false; + this.result = null; + } + + _getResultIterator() { + return this.result[Symbol.iterator](); + } +} + +class LinqIterable extends BaseLinqIterable { + constructor(source) { + super(); + if (Array.isArray(source)) { + this.isResulted = true; + this.result = source; + } + this.source = source; } - _getResultIterator() { - return this.result[Symbol.iterator](); + [Symbol.iterator]() { + return this.source[Symbol.iterator](); } } -class LinqIterable extends BaseLinqIterable { +class ArrayLikeIterable extends BaseLinqIterable { constructor(source) { super(); if (Array.isArray(source)) { @@ -91,7 +107,23 @@ class LinqIterable extends BaseLinqIterable { } [Symbol.iterator]() { - return this.source[Symbol.iterator](); + if (this.isResulted) { + return this._getResultIterator(); + } + const length = this.source.length; + const source = this.source; + let current = 0; + return { + next() { + if (current < length) { + const value = source[current]; + current++; + return { done: false, value }; + } else { + return { done: true }; + } + } + }; } } @@ -103,6 +135,14 @@ function fromObject(obj) { return new LinqIterable(Object.entries(obj)); } +/** + * The object which has property length of type number and keys with names: '0', '1' ... + * @param {ArrayLike} source + */ +function fromArrayLike(source) { + return new ArrayLikeIterable(source); +} + function range(from, to) { return new RangeIterable(from, to); } @@ -111,45 +151,45 @@ function range(from, to) { * Return filtred array [1, 2, 3, 4].where(x => x % 2 === 0) === [2, 4] */ class WhereIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {Function} predicate - */ + /** + * + * @param {Iterable} source + * @param {Function} predicate + */ constructor(source, predicate) { - super(); - if (Array.isArray(source)) { - this.isResulted = true; - this.result = source.filter(predicate); - } - this.source = source; - this.predicate = predicate; - } - - [Symbol.iterator]() { - if (this.isResulted) { - return this._getResultIterator(); - } - const iterator = this.source[Symbol.iterator](); - const predicate = this.predicate; - return { - next() { - while (true) { - const { done, value } = iterator.next(); - if (done) { - return { - done: true - }; - } - if (predicate(value)) { - return { - done: false, - value - }; - } - } - } - }; + super(); + if (Array.isArray(source)) { + this.isResulted = true; + this.result = source.filter(predicate); + } + this.source = source; + this.predicate = predicate; + } + + [Symbol.iterator]() { + if (this.isResulted) { + return this._getResultIterator(); + } + const iterator = this.source[Symbol.iterator](); + const predicate = this.predicate; + return { + next() { + while (true) { + const { done, value } = iterator.next(); + if (done) { + return { + done: true + }; + } + if (predicate(value)) { + return { + done: false, + value + }; + } + } + } + }; } } @@ -157,41 +197,41 @@ class WhereIterable extends BaseLinqIterable { * Return mapped array [1, 2, 3].select(x => x * 2) === [2, 4, 6] */ class SelectIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {Function} map - */ + /** + * + * @param {Iterable} source + * @param {Function} map + */ constructor(source, map) { - super(); - if (Array.isArray(source)) { - this.isResulted = true; - this.result = source.map(map); - } - this.source = source; - this.map = map; - } - - [Symbol.iterator]() { - if (this.isResulted) { - return this._getResultIterator(); - } - const iterator = this.source[Symbol.iterator](); - const map = this.map; - return { - next() { - const { done, value } = iterator.next(); - if (done) { - return { - done: true - }; - } - return { - done: false, - value: map(value) - }; - } - }; + super(); + if (Array.isArray(source)) { + this.isResulted = true; + this.result = source.map(map); + } + this.source = source; + this.map = map; + } + + [Symbol.iterator]() { + if (this.isResulted) { + return this._getResultIterator(); + } + const iterator = this.source[Symbol.iterator](); + const map = this.map; + return { + next() { + const { done, value } = iterator.next(); + if (done) { + return { + done: true + }; + } + return { + done: false, + value: map(value) + }; + } + }; } } @@ -199,30 +239,30 @@ class SelectIterable extends BaseLinqIterable { * Return flatten mapped array [[1, 2], [3, 4]].selectMany(x => x) === [1, 2, 3, 4, 5] */ class SelectManyIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {Function} extract - */ + /** + * + * @param {Iterable} source + * @param {Function} extract + */ constructor(source, extract) { super(); - this.source = source; - this.extract = extract; + this.source = source; + this.extract = extract; } - [Symbol.iterator]() { - const iterator = this.source[Symbol.iterator](); + [Symbol.iterator]() { + const iterator = this.source[Symbol.iterator](); const extract = this.extract; let isSubDone = true; let subIterator = null; - return { - next() { + return { + next() { const item = SelectManyIterable.getNextItem(iterator, extract, subIterator, isSubDone); isSubDone = item.sdone; subIterator = item.sIterator; return item.value; - } - }; + } + }; } static getSecondaryIterator(mainIterator, extract) { @@ -239,7 +279,7 @@ class SelectManyIterable extends BaseLinqIterable { } return { iterator: secondaryIterator, first: secondaryItem.value, final: false }; } - + static getNextItem(mainIterator, extract, subIterator, isSubDone) { if (isSubDone) { const { iterator, first, final } = SelectManyIterable.getSecondaryIterator(mainIterator, extract); @@ -252,26 +292,26 @@ class SelectManyIterable extends BaseLinqIterable { if (snext.done) { return SelectManyIterable.getNextItem(mainIterator, extract, null, true); } - return { value: { done: false, value: snext.value }, sIterator: subIterator, sdone: false }; + return { value: { done: false, value: snext.value }, sIterator: subIterator, sdone: false }; } } } class FirstFinalizer { - static get (iterable) { + static get(iterable) { const iterator = iterable[Symbol.iterator](); const { value } = iterator.next(); return value; } - static getOrDefault (iterable, def) { + static getOrDefault(iterable, def) { const iterator = iterable[Symbol.iterator](); const { value, done } = iterator.next(); return done ? def : value; } - static getOrThrow (iterable, def) { + static getOrThrow(iterable, def) { const iterator = iterable[Symbol.iterator](); const { value, done } = iterator.next(); if (done) { @@ -282,16 +322,16 @@ class FirstFinalizer { } class SingleFinalizer { - static get (iterable) { + static get(iterable) { const iterator = iterable[Symbol.iterator](); const { value, done } = iterator.next(); - if (done || !iterator.next().done ) { + if (done || !iterator.next().done) { throw new RangeError('Sequence does not contain single item'); } return value; } - static getOrDefault (iterable, def) { + static getOrDefault(iterable, def) { const iterator = iterable[Symbol.iterator](); const { value, done } = iterator.next(); if (!iterator.next().done) { @@ -305,41 +345,41 @@ class SingleFinalizer { * Return first N numbers of source */ class TakeIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {number} count - */ + /** + * + * @param {Iterable} source + * @param {number} count + */ constructor(source, count) { - super(); - if (Array.isArray(source)) { - this.isResulted = true; - this.result = source.slice(0, count); - } - this.source = source; - this.count = count; - } - - [Symbol.iterator]() { - if (this.isResulted) { - return this._getResultIterator(); - } - const iterator = this.source[Symbol.iterator](); + super(); + if (Array.isArray(source)) { + this.isResulted = true; + this.result = source.slice(0, count); + } + this.source = source; + this.count = count; + } + + [Symbol.iterator]() { + if (this.isResulted) { + return this._getResultIterator(); + } + const iterator = this.source[Symbol.iterator](); const count = this.count; let fetched = 0; - return { - next() { + return { + next() { if (fetched < count) { const { done, value } = iterator.next(); - fetched++; + fetched++; if (done) { return { done: true }; } return { done: false, value }; } return { done: true }; - } - }; + } + }; } } @@ -347,30 +387,30 @@ class TakeIterable extends BaseLinqIterable { * Skip first N numbers of source and return the rest */ class SkipIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {number} count - */ + /** + * + * @param {Iterable} source + * @param {number} count + */ constructor(source, count) { super(); if (Array.isArray(source)) { this.isResulted = true; this.result = source.slice(count, source.length); } - this.source = source; - this.count = count; - } + this.source = source; + this.count = count; + } - [Symbol.iterator]() { + [Symbol.iterator]() { if (this.isResulted) { return this._getResultIterator(); } - const iterator = this.source[Symbol.iterator](); + const iterator = this.source[Symbol.iterator](); const count = this.count; let skipped = 0; - return { - next() { + return { + next() { if (skipped == 0) { // first get. while (skipped < count) { @@ -382,69 +422,78 @@ class SkipIterable extends BaseLinqIterable { } } return iterator.next(); - } - }; - } -} - -class AllFinalizer { - static get(source, predicate) { - for (const item of source) { - if (!predicate(item)) { - return false; } - } - return true; + }; } +} - static getAllAndEvery(source, predicate) { - let hasItems = false; - for (const item of source) { - hasItems = true; - if (!predicate(item)) { - return false; - } - } - return hasItems; - } +class AllFinalizer { + static get(source, predicate) { + if (Array.isArray(source)) { + return source.every(predicate); + } + for (const item of source) { + if (!predicate(item)) { + return false; + } + } + return true; + } + + static getAllAndEvery(source, predicate) { + if (Array.isArray(source)) { + return source.length > 0 && source.every(predicate); + } + let hasItems = false; + for (const item of source) { + hasItems = true; + if (!predicate(item)) { + return false; + } + } + return hasItems; + } } -class AnyFinalizer { - static get(source, predicate) { - for (const item of source) { - if (predicate(item)) { - return true; - } - } - return false; - } +class AnyFinalizer { + static get(source, predicate) { + if (Array.isArray(source)) { + return source.some(predicate); + } + for (const item of source) { + if (predicate(item)) { + return true; + } + } + return false; + } } /** * Returns distinct values */ class DistinctIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {Function} comparer comparer function. if not provider use native Set. - */ + /** + * + * @param {Iterable} source + * @param {Function} comparer comparer function. if not provider use native Set. + */ constructor(source, comparer) { super(); - this.source = source; - this.comparer = comparer; + this.source = source; + this.comparer = comparer; } - [Symbol.iterator]() { + [Symbol.iterator]() { if (!this.comparer) { var set = new Set(this.source); - return set[Symbol.iterator](); + return set[Symbol.iterator](); } const iterator = this.source[Symbol.iterator](); const itemChecker = new DistinctItemChecker(this.comparer); - return { - next() { - while(true) { + return { + next() { + while (true) { const { done, value } = iterator.next(); if (done) { return { done: true }; @@ -455,8 +504,8 @@ class DistinctIterable extends BaseLinqIterable { itemChecker.add(value); return { done: false, value }; } - } - }; + } + }; } } @@ -471,7 +520,107 @@ class DistinctItemChecker { } has(item) { - return this.list.some(_ => this.comparer(_, item)); + return this.list.some(_ => this.comparer(_, item)); + } +} + +class Grouping extends BaseLinqIterable { + constructor(key, source) { + super(); + this.key = key; + this.source = source.toArray(); + this.isResulted = true; + this.result = this.source; + } + + [Symbol.iterator]() { + const iterator = this._getResultIterator(); + return { + next() { + return iterator.next(); + } + } + } +} + +class GroupIterable extends BaseLinqIterable { + constructor(source, keySelector, elementSelector, resultCreator) { + super(); + this.source = source; + if (typeof keySelector === 'undefined') { + throw new Error('keyselector is required'); + } + this.keySelector = keySelector; + if (typeof elementSelector === 'function' && elementSelector.length === 2) { + this.resultCreator = elementSelector; + } else { + this.elementSelector = elementSelector; + this.resultCreator = resultCreator; + } + } + + __group() { + const map = new Map(); + const elementSelector = typeof this.elementSelector === 'undefined' ? _ => _ : this.elementSelector; + for (const item of this.source) { + const key = this.keySelector(item); + const element = elementSelector(item); + let value = map.get(key); + if (typeof value === 'undefined') { + value = [ element ]; + } else { + value.push(element); + } + map.set(key, value); + } + return map; + } + + [Symbol.iterator]() { + const groupIterator = this.__group()[Symbol.iterator](); + const resultCreator = typeof this.resultCreator === 'undefined' ? (key, grouping) => (new Grouping(key, grouping)) : this.resultCreator; + return { + next() { + const { done, value } = groupIterator.next(); + if (done) { + return { done: true }; + } + const [ key, grouping ] = value; + const linqGrouping = fromIterable(grouping); + const result = resultCreator(key, linqGrouping); + return { + done: false, + value: result + }; + } + } + } +} + +class CountFinalizer { + static get(source, predicate) { + if (Array.isArray(source)) { + if (predicate) { + return source.filter(predicate).length; + } + return source.length; + } + let i = 0; + const iterator = source[Symbol.iterator](); + while (true) { + let { done, value } = iterator.next(); + if (done) { + return i; + } else { + if (predicate) { + if (predicate(value)) { + i++; + } + } else { + i++; + } + } + } } } @@ -499,18 +648,25 @@ const linqMixin = { }, ofType(type) { if (typeof type === 'string') { - return new WhereIterable(this, function (item) { return typeof item === type; }); + return new WhereIterable(this, function (item) { + return typeof item === type; + }); } else { - return new WhereIterable(this, function (item) { return item instanceof type; }); + return new WhereIterable(this, function (item) { + return item instanceof type; + }); } }, + groupBy(keySelector, elementSelector, resultCreator) { + return new GroupIterable(this, keySelector, elementSelector, resultCreator); + }, toArray() { return this.isResulted ? this.result : Array.from(this); }, toMap(keySelector, valueSelector) { const transformValue = typeof valueSelector === 'undefined'; return new Map(this.select(_ => [ - keySelector(_), + keySelector(_), transformValue ? _ : valueSelector(_) ]) ); @@ -542,17 +698,26 @@ const linqMixin = { any(predicate) { return AnyFinalizer.get(this, predicate) }, + count(predicate) { + return CountFinalizer.get(this, predicate); + } }; -applyMixin(LinqIterable, linqMixin); -applyMixin(WhereIterable, linqMixin); -applyMixin(SelectIterable, linqMixin); -applyMixin(SelectManyIterable, linqMixin); -applyMixin(TakeIterable, linqMixin); -applyMixin(SkipIterable, linqMixin); -applyMixin(RangeIterable, linqMixin); -applyMixin(DistinctIterable, linqMixin); - +applyMixin(linqMixin, [ + LinqIterable, + ArrayLikeIterable, + WhereIterable, + SelectIterable, + SelectManyIterable, + TakeIterable, + SkipIterable, + RangeIterable, + DistinctIterable, + Grouping, + GroupIterable, +]); + +exports.fromArrayLike = fromArrayLike; exports.fromIterable = fromIterable; exports.fromObject = fromObject; exports.range = range; diff --git a/src/creation.js b/src/creation.js index 8e863e5..2264a93 100644 --- a/src/creation.js +++ b/src/creation.js @@ -16,6 +16,37 @@ export class LinqIterable extends BaseLinqIterable { } } +export class ArrayLikeIterable extends BaseLinqIterable { + constructor(source) { + super(); + if (Array.isArray(source)) { + this.isResulted = true; + this.result = source; + } + this.source = source; + } + + [Symbol.iterator]() { + if (this.isResulted) { + return this._getResultIterator(); + } + const length = this.source.length; + const source = this.source; + let current = 0; + return { + next() { + if (current < length) { + const value = source[current]; + current++; + return { done: false, value }; + } else { + return { done: true }; + } + } + }; + } +} + export function fromIterable(source) { return new LinqIterable(source); } @@ -24,6 +55,14 @@ export function fromObject(obj) { return new LinqIterable(Object.entries(obj)); } +/** + * The object which has property length of type number and keys with names: '0', '1' ... + * @param {ArrayLike} source + */ +export function fromArrayLike(source) { + return new ArrayLikeIterable(source); +} + export function range(from, to) { return new RangeIterable(from, to); } diff --git a/src/finalizers/first.js b/src/finalizers/first.js index dc2e2b3..13492ee 100644 --- a/src/finalizers/first.js +++ b/src/finalizers/first.js @@ -1,17 +1,17 @@ export class FirstFinalizer { - static get (iterable) { + static get(iterable) { const iterator = iterable[Symbol.iterator](); const { value } = iterator.next(); return value; } - static getOrDefault (iterable, def) { + static getOrDefault(iterable, def) { const iterator = iterable[Symbol.iterator](); const { value, done } = iterator.next(); return done ? def : value; } - static getOrThrow (iterable, def) { + static getOrThrow(iterable, def) { const iterator = iterable[Symbol.iterator](); const { value, done } = iterator.next(); if (done) { diff --git a/src/finalizers/single.js b/src/finalizers/single.js index 6ab206f..b07d3e9 100644 --- a/src/finalizers/single.js +++ b/src/finalizers/single.js @@ -1,14 +1,14 @@ export class SingleFinalizer { - static get (iterable) { + static get(iterable) { const iterator = iterable[Symbol.iterator](); const { value, done } = iterator.next(); - if (done || !iterator.next().done ) { + if (done || !iterator.next().done) { throw new RangeError('Sequence does not contain single item'); } return value; } - static getOrDefault (iterable, def) { + static getOrDefault(iterable, def) { const iterator = iterable[Symbol.iterator](); const { value, done } = iterator.next(); if (!iterator.next().done) { diff --git a/src/generators/range.js b/src/generators/range.js index b326b34..a07bc45 100644 --- a/src/generators/range.js +++ b/src/generators/range.js @@ -2,51 +2,55 @@ * Generates range of numbers [from, to) */ export class RangeIterable { - /** - * The range is [from, to) - * @param {number} from - * @param {number} to - */ + /** + * The range is [from, to) + * @param {number} from + * @param {number} to + */ constructor(from, to) { this.from = from; this.to = to; } - + __ascendingRange() { const to = this.to; let current = this.from; - return { - next() { + return { + next() { if (current < to) { return { done: false, value: current++ }; } else { return { done: true }; } - } - }; + } + }; } __descendingRange() { const to = this.to; let current = this.from; - return { - next() { + return { + next() { if (current > to) { return { done: false, value: current-- }; } else { return { done: true }; } - } - }; + } + }; } - [Symbol.iterator]() { + [Symbol.iterator]() { if (this.from < this.to) { return this.__ascendingRange(); } if (this.from > this.to) { return this.__descendingRange(); } - return { next() { return { done: true } } }; + return { + next() { + return { done: true } + } + }; } } diff --git a/src/index.js b/src/index.js index c8e67ef..f055970 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ import { applyMixin } from './utils'; -import { LinqIterable } from './creation'; +import { ArrayLikeIterable, LinqIterable } from './creation'; import { linqMixin } from './linq-mixin'; import { WhereIterable } from './iterables/where'; import { SelectIterable } from './iterables/select'; @@ -8,17 +8,20 @@ import { TakeIterable } from './iterables/take'; import { SkipIterable } from './iterables/skip'; import { RangeIterable } from './generators/range'; import { DistinctIterable } from './iterables/distinct'; -import { GroupIterable, Grouping } from './iterables/group'; +import { Grouping, GroupIterable } from './iterables/group'; -applyMixin(LinqIterable, linqMixin); -applyMixin(WhereIterable, linqMixin); -applyMixin(SelectIterable, linqMixin); -applyMixin(SelectManyIterable, linqMixin); -applyMixin(TakeIterable, linqMixin); -applyMixin(SkipIterable, linqMixin); -applyMixin(RangeIterable, linqMixin); -applyMixin(DistinctIterable, linqMixin); -applyMixin(Grouping, linqMixin); -applyMixin(GroupIterable, linqMixin); +applyMixin(linqMixin, [ + LinqIterable, + ArrayLikeIterable, + WhereIterable, + SelectIterable, + SelectManyIterable, + TakeIterable, + SkipIterable, + RangeIterable, + DistinctIterable, + Grouping, + GroupIterable, +]); -export { fromIterable, fromObject, range } from './creation'; +export { fromIterable, fromObject, fromArrayLike, range } from './creation'; diff --git a/src/iterables/distinct.js b/src/iterables/distinct.js index 6cf7139..63bf35d 100644 --- a/src/iterables/distinct.js +++ b/src/iterables/distinct.js @@ -4,27 +4,27 @@ import { BaseLinqIterable } from "../base-linq-iterable"; * Returns distinct values */ export class DistinctIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {Function} comparer comparer function. if not provider use native Set. - */ + /** + * + * @param {Iterable} source + * @param {Function} comparer comparer function. if not provider use native Set. + */ constructor(source, comparer) { super(); - this.source = source; - this.comparer = comparer; + this.source = source; + this.comparer = comparer; } - [Symbol.iterator]() { + [Symbol.iterator]() { if (!this.comparer) { var set = new Set(this.source); - return set[Symbol.iterator](); + return set[Symbol.iterator](); } const iterator = this.source[Symbol.iterator](); const itemChecker = new DistinctItemChecker(this.comparer); - return { - next() { - while(true) { + return { + next() { + while (true) { const { done, value } = iterator.next(); if (done) { return { done: true }; @@ -35,8 +35,8 @@ export class DistinctIterable extends BaseLinqIterable { itemChecker.add(value); return { done: false, value }; } - } - }; + } + }; } } @@ -51,6 +51,6 @@ class DistinctItemChecker { } has(item) { - return this.list.some(_ => this.comparer(_, item)); + return this.list.some(_ => this.comparer(_, item)); } } diff --git a/src/iterables/group.js b/src/iterables/group.js index 3226bbf..c442b6a 100644 --- a/src/iterables/group.js +++ b/src/iterables/group.js @@ -35,7 +35,7 @@ export class GroupIterable extends BaseLinqIterable { this.resultCreator = resultCreator; } } - + __group() { const map = new Map(); const elementSelector = typeof this.elementSelector === 'undefined' ? _ => _ : this.elementSelector; @@ -55,14 +55,14 @@ export class GroupIterable extends BaseLinqIterable { [Symbol.iterator]() { const groupIterator = this.__group()[Symbol.iterator](); - const resultCreator = typeof this.resultCreator === 'undefined' ? (key, grouping) => ( new Grouping(key, grouping) ) : this.resultCreator; + const resultCreator = typeof this.resultCreator === 'undefined' ? (key, grouping) => (new Grouping(key, grouping)) : this.resultCreator; return { next() { const { done, value } = groupIterator.next(); if (done) { return { done: true }; } - const [key, grouping] = value; + const [ key, grouping ] = value; const linqGrouping = fromIterable(grouping); const result = resultCreator(key, linqGrouping); return { diff --git a/src/iterables/select-many.js b/src/iterables/select-many.js index c53d8b1..21d6518 100644 --- a/src/iterables/select-many.js +++ b/src/iterables/select-many.js @@ -4,30 +4,30 @@ import { BaseLinqIterable } from "../base-linq-iterable"; * Return flatten mapped array [[1, 2], [3, 4]].selectMany(x => x) === [1, 2, 3, 4, 5] */ export class SelectManyIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {Function} extract - */ + /** + * + * @param {Iterable} source + * @param {Function} extract + */ constructor(source, extract) { super(); - this.source = source; - this.extract = extract; + this.source = source; + this.extract = extract; } - [Symbol.iterator]() { - const iterator = this.source[Symbol.iterator](); + [Symbol.iterator]() { + const iterator = this.source[Symbol.iterator](); const extract = this.extract; let isSubDone = true; let subIterator = null; - return { - next() { + return { + next() { const item = SelectManyIterable.getNextItem(iterator, extract, subIterator, isSubDone); isSubDone = item.sdone; subIterator = item.sIterator; return item.value; - } - }; + } + }; } static getSecondaryIterator(mainIterator, extract) { @@ -44,7 +44,7 @@ export class SelectManyIterable extends BaseLinqIterable { } return { iterator: secondaryIterator, first: secondaryItem.value, final: false }; } - + static getNextItem(mainIterator, extract, subIterator, isSubDone) { if (isSubDone) { const { iterator, first, final } = SelectManyIterable.getSecondaryIterator(mainIterator, extract); @@ -57,7 +57,7 @@ export class SelectManyIterable extends BaseLinqIterable { if (snext.done) { return SelectManyIterable.getNextItem(mainIterator, extract, null, true); } - return { value: { done: false, value: snext.value }, sIterator: subIterator, sdone: false }; + return { value: { done: false, value: snext.value }, sIterator: subIterator, sdone: false }; } } diff --git a/src/iterables/select.js b/src/iterables/select.js index f37d680..0139663 100644 --- a/src/iterables/select.js +++ b/src/iterables/select.js @@ -4,40 +4,40 @@ import { BaseLinqIterable } from "../base-linq-iterable"; * Return mapped array [1, 2, 3].select(x => x * 2) === [2, 4, 6] */ export class SelectIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {Function} map - */ + /** + * + * @param {Iterable} source + * @param {Function} map + */ constructor(source, map) { - super(); - if (Array.isArray(source)) { - this.isResulted = true; - this.result = source.map(map); - } - this.source = source; - this.map = map; - } + super(); + if (Array.isArray(source)) { + this.isResulted = true; + this.result = source.map(map); + } + this.source = source; + this.map = map; + } - [Symbol.iterator]() { - if (this.isResulted) { - return this._getResultIterator(); - } - const iterator = this.source[Symbol.iterator](); - const map = this.map; - return { - next() { - const { done, value } = iterator.next(); - if (done) { - return { - done: true - }; - } - return { - done: false, - value: map(value) - }; - } - }; + [Symbol.iterator]() { + if (this.isResulted) { + return this._getResultIterator(); + } + const iterator = this.source[Symbol.iterator](); + const map = this.map; + return { + next() { + const { done, value } = iterator.next(); + if (done) { + return { + done: true + }; + } + return { + done: false, + value: map(value) + }; + } + }; } } diff --git a/src/iterables/skip.js b/src/iterables/skip.js index db46f93..a0191ce 100644 --- a/src/iterables/skip.js +++ b/src/iterables/skip.js @@ -4,30 +4,30 @@ import { BaseLinqIterable } from "../base-linq-iterable"; * Skip first N numbers of source and return the rest */ export class SkipIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {number} count - */ + /** + * + * @param {Iterable} source + * @param {number} count + */ constructor(source, count) { super(); if (Array.isArray(source)) { this.isResulted = true; this.result = source.slice(count, source.length); } - this.source = source; - this.count = count; - } + this.source = source; + this.count = count; + } - [Symbol.iterator]() { + [Symbol.iterator]() { if (this.isResulted) { return this._getResultIterator(); } - const iterator = this.source[Symbol.iterator](); + const iterator = this.source[Symbol.iterator](); const count = this.count; let skipped = 0; - return { - next() { + return { + next() { if (skipped == 0) { // first get. while (skipped < count) { @@ -39,7 +39,7 @@ export class SkipIterable extends BaseLinqIterable { } } return iterator.next(); - } - }; + } + }; } } diff --git a/src/iterables/take.js b/src/iterables/take.js index ea1e34e..ecbe843 100644 --- a/src/iterables/take.js +++ b/src/iterables/take.js @@ -4,40 +4,40 @@ import { BaseLinqIterable } from "../base-linq-iterable"; * Return first N numbers of source */ export class TakeIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {number} count - */ + /** + * + * @param {Iterable} source + * @param {number} count + */ constructor(source, count) { - super(); - if (Array.isArray(source)) { - this.isResulted = true; - this.result = source.slice(0, count); - } - this.source = source; - this.count = count; - } + super(); + if (Array.isArray(source)) { + this.isResulted = true; + this.result = source.slice(0, count); + } + this.source = source; + this.count = count; + } - [Symbol.iterator]() { - if (this.isResulted) { - return this._getResultIterator(); - } - const iterator = this.source[Symbol.iterator](); + [Symbol.iterator]() { + if (this.isResulted) { + return this._getResultIterator(); + } + const iterator = this.source[Symbol.iterator](); const count = this.count; let fetched = 0; - return { - next() { + return { + next() { if (fetched < count) { const { done, value } = iterator.next(); - fetched++; + fetched++; if (done) { return { done: true }; } return { done: false, value }; } return { done: true }; - } - }; + } + }; } } diff --git a/src/iterables/where.js b/src/iterables/where.js index 2f0a60d..a53479f 100644 --- a/src/iterables/where.js +++ b/src/iterables/where.js @@ -4,44 +4,44 @@ import { BaseLinqIterable } from "../base-linq-iterable"; * Return filtred array [1, 2, 3, 4].where(x => x % 2 === 0) === [2, 4] */ export class WhereIterable extends BaseLinqIterable { - /** - * - * @param {Iterable} source - * @param {Function} predicate - */ + /** + * + * @param {Iterable} source + * @param {Function} predicate + */ constructor(source, predicate) { - super(); - if (Array.isArray(source)) { - this.isResulted = true; - this.result = source.filter(predicate); - } - this.source = source; - this.predicate = predicate; - } + super(); + if (Array.isArray(source)) { + this.isResulted = true; + this.result = source.filter(predicate); + } + this.source = source; + this.predicate = predicate; + } - [Symbol.iterator]() { - if (this.isResulted) { - return this._getResultIterator(); - } - const iterator = this.source[Symbol.iterator](); - const predicate = this.predicate; - return { - next() { - while (true) { - const { done, value } = iterator.next(); - if (done) { - return { - done: true - }; - } - if (predicate(value)) { - return { - done: false, - value - }; - } - } - } - }; + [Symbol.iterator]() { + if (this.isResulted) { + return this._getResultIterator(); + } + const iterator = this.source[Symbol.iterator](); + const predicate = this.predicate; + return { + next() { + while (true) { + const { done, value } = iterator.next(); + if (done) { + return { + done: true + }; + } + if (predicate(value)) { + return { + done: false, + value + }; + } + } + } + }; } } diff --git a/src/linq-mixin.js b/src/linq-mixin.js index 5ac1e3f..ec21f65 100644 --- a/src/linq-mixin.js +++ b/src/linq-mixin.js @@ -35,9 +35,13 @@ export const linqMixin = { }, ofType(type) { if (typeof type === 'string') { - return new WhereIterable(this, function (item) { return typeof item === type; }); + return new WhereIterable(this, function (item) { + return typeof item === type; + }); } else { - return new WhereIterable(this, function (item) { return item instanceof type; }); + return new WhereIterable(this, function (item) { + return item instanceof type; + }); } }, groupBy(keySelector, elementSelector, resultCreator) { @@ -49,7 +53,7 @@ export const linqMixin = { toMap(keySelector, valueSelector) { const transformValue = typeof valueSelector === 'undefined'; return new Map(this.select(_ => [ - keySelector(_), + keySelector(_), transformValue ? _ : valueSelector(_) ]) ); @@ -84,4 +88,4 @@ export const linqMixin = { count(predicate) { return CountFinalizer.get(this, predicate); } -} +}; diff --git a/src/utils.js b/src/utils.js index 6846542..613fdec 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,13 +1,10 @@ /** * Apply mixin to a class - * @param {Function} destination - * @param {Function} mixin + * @param {object} mixin + * @param {Function[]} destinations */ -export function applyMixin(destination, mixin) { - for (const prop in mixin) { - if (prop === 'constructor' || !mixin.hasOwnProperty(prop)) { - continue; - } - destination.prototype[prop] = mixin[prop]; +export function applyMixin(mixin, destinations) { + for (const dest of destinations) { + Object.assign(dest.prototype, mixin); } } diff --git a/test/benchmark/select.perf.js b/test/benchmark/select.perf.js index bb93d4b..78665a6 100644 --- a/test/benchmark/select.perf.js +++ b/test/benchmark/select.perf.js @@ -1,4 +1,4 @@ -const { range, fromIterable } = require('../../index'); +const { range, fromIterable, fromArrayLike } = require('../../index'); const Benchmark = require('benchmark'); const suite = new Benchmark.Suite(); @@ -6,17 +6,16 @@ const arrayInput = range(0, 100000).toArray(); const linqInput = fromIterable(arrayInput); suite -.add('Array map', () => { - arrayInput.filter(_ => _ % 2 === 0).map(_ => _ * 3); -}) -.add('select', () => { - linqInput.where(_ => _ % 2 === 0).select(_ => _ * 3).toArray(); -}) -.on('complete', function () { - const res0 = this[0]; - const res1 = this[1]; - console.log(res0.toString()); - console.log(res1.toString()); -}) -.run({ 'async': true }); + .add('Array map', () => { + arrayInput.filter(_ => _ % 2 === 0).map(_ => _ * 3); + }) + .add('select', () => { + linqInput.where(_ => _ % 2 === 0).select(_ => _ * 3).toArray(); + }) + .on('complete', function () { + for (const bench of fromArrayLike(this)) { + console.log(bench.toString()); + } + }) + .run({ 'async': true }); diff --git a/test/benchmark/take.perf.js b/test/benchmark/take.perf.js index 67ff17f..d643a19 100644 --- a/test/benchmark/take.perf.js +++ b/test/benchmark/take.perf.js @@ -6,17 +6,17 @@ const arrayInput = range(0, 100000).toArray(); const takeInput = fromIterable(arrayInput); suite -.add('Array slice', () => { - arrayInput.slice(0, 1000); -}) -.add('take', () => { - takeInput.take(1000).toArray(); -}) -.on('complete', function () { - const res0 = this[0]; - const res1 = this[1]; - console.log(res0.toString()); - console.log(res1.toString()); -}) -.run({ 'async': true }); + .add('Array slice', () => { + arrayInput.slice(0, 1000); + }) + .add('take', () => { + takeInput.take(1000).toArray(); + }) + .on('complete', function () { + const res0 = this[0]; + const res1 = this[1]; + console.log(res0.toString()); + console.log(res1.toString()); + }) + .run({ 'async': true }); diff --git a/test/unit/first.spec.js b/test/unit/first.spec.js index 8750324..6e577be 100644 --- a/test/unit/first.spec.js +++ b/test/unit/first.spec.js @@ -24,7 +24,9 @@ describe('first finalizer', () => { }); it('should firstOrThrow throw exception if not items', () => { - const val = function () { return fromIterable([]).firstOrThrow(); }; + const val = function () { + return fromIterable([]).firstOrThrow(); + }; expect(val).to.throw(RangeError); }); diff --git a/test/unit/from.spec.js b/test/unit/from.spec.js index 2ebda1b..557da0a 100644 --- a/test/unit/from.spec.js +++ b/test/unit/from.spec.js @@ -1,8 +1,8 @@ import { assert, expect } from 'chai'; -import { fromIterable, fromObject } from "../../src"; +import { fromArrayLike, fromIterable, fromObject } from "../../src"; describe('from creation tests', () => { - it('should create a select js iterable from collection', () => { + it('should create a linq iterable from collection', () => { const val = fromIterable([1, 2, 3]); assert.isTrue(typeof val.where === 'function'); assert.isTrue(typeof val.select === 'function'); @@ -11,7 +11,7 @@ describe('from creation tests', () => { assert.isTrue(typeof val.single === 'function'); }); - it('should create a select js iterable from object', () => { + it('should create a linq iterable from object', () => { const val = fromObject({ 'a': 1, 'b': 2, 10: 3 }); assert.isTrue(typeof val.where === 'function'); assert.isTrue(typeof val.select === 'function'); @@ -19,6 +19,20 @@ describe('from creation tests', () => { assert.isTrue(typeof val.first === 'function'); assert.isTrue(typeof val.single === 'function'); const res = val.toArray(); - expect(res).to.deep.equal([ ['10', 3], ['a', 1], ['b', 2] ]); + expect(res).to.deep.equal([['10', 3], ['a', 1], ['b', 2]]); }); -}); \ No newline at end of file + + it('should create a linq iterable from ArrayLike object', () => { + const input = { + 0: 'A', + '1': 'B', + 2: 'C', + '3': 'D', + 'name': 'E', + 'something': 'F', + 'length': 4 + }; + const seq = fromArrayLike(input).toArray().join(','); + expect(seq).to.equal('A,B,C,D'); + }); +}); diff --git a/test/unit/group.spec.js b/test/unit/group.spec.js index 2c5495c..650d840 100644 --- a/test/unit/group.spec.js +++ b/test/unit/group.spec.js @@ -8,6 +8,7 @@ describe('groupBy tests', () => { this.name = name; } } + let input = []; beforeEach(() => { input = [ @@ -21,7 +22,9 @@ describe('groupBy tests', () => { }); it('should throw exception if no keySelector is provided', () => { - const func = function() { return range(0, 1).groupBy(); }; + const func = function () { + return range(0, 1).groupBy(); + }; expect(func).to.throw(Error); }); @@ -29,7 +32,7 @@ describe('groupBy tests', () => { const res = fromIterable(input).groupBy(_ => _.age).toArray(); expect(res.length).to.equal(3); for (const gr of res) { - switch(gr.key) { + switch (gr.key) { case 10: { expect(gr.count()).to.equal(3); break; @@ -52,7 +55,7 @@ describe('groupBy tests', () => { const res = fromIterable(input).groupBy(_ => _.age, _ => _.name).toArray(); expect(res.length).to.equal(3); for (const gr of res) { - switch(gr.key) { + switch (gr.key) { case 10: { expect(gr.count()).to.equal(3); expect(gr.toArray()).to.deep.equal(['A', 'C', 'E']); diff --git a/test/unit/of-type.spec.js b/test/unit/of-type.spec.js index 031a7dc..fa378dc 100644 --- a/test/unit/of-type.spec.js +++ b/test/unit/of-type.spec.js @@ -3,7 +3,7 @@ import { fromIterable } from "../../src"; describe('oftype tests', () => { it('should check for primary types', () => { - const source = [ 1, 2, 3, 'a', 4, 5, 's', true, 'c', false ]; + const source = [1, 2, 3, 'a', 4, 5, 's', true, 'c', false]; const inputNumber = fromIterable(source).ofType('number'); expect(Array.from(inputNumber)).to.deep.equal([1, 2, 3, 4, 5]); const inputStr = fromIterable(source).ofType('string'); @@ -14,10 +14,13 @@ describe('oftype tests', () => { }); it('should check for function types', () => { - class A {} - class B {} + class A { + } - const source = [ new A(), new B() ]; + class B { + } + + const source = [new A(), new B()]; const result = fromIterable(source).ofType(A); const arr = Array.from(result); expect(arr.length).to.equal(1); diff --git a/test/unit/range.spec.js b/test/unit/range.spec.js index 2c5ea36..49d1f5c 100644 --- a/test/unit/range.spec.js +++ b/test/unit/range.spec.js @@ -2,22 +2,22 @@ import { expect } from 'chai'; import { range } from "../../src"; describe('range tests', () => { - it ('should generate a range', () => { + it('should generate a range', () => { const output = range(0, 5).toArray(); expect(output).to.deep.equal([0, 1, 2, 3, 4]); }); - it ('should be able to continue with another operation', () => { + it('should be able to continue with another operation', () => { const output = range(0, 5).skip(2).toArray(); expect(output).to.deep.equal([2, 3, 4]); }); - it ('should generate decreasing range when from bigger than to', () => { + it('should generate decreasing range when from bigger than to', () => { const output = range(5, 0).toArray(); expect(output).to.deep.equal([5, 4, 3, 2, 1]); }); - it ('should return empty when from === to', () => { + it('should return empty when from === to', () => { const output = range(5, 5).toArray(); expect(output).to.deep.equal([]); }); diff --git a/test/unit/select.spec.js b/test/unit/select.spec.js index e11758f..4915be3 100644 --- a/test/unit/select.spec.js +++ b/test/unit/select.spec.js @@ -12,7 +12,7 @@ describe('select tests', () => { const input = fromIterable(new Set(['a', 'b', 'c', 'd'])).select(_ => _ + 'a'); const result = Array.from(input); expect(result).to.deep.equal(['aa', 'ba', 'ca', 'da']); - }); + }); it('should map after filter', () => { const input = fromIterable(['a', 'b', 'c', 'd']).where(_ => _ !== 'a').select(_ => _ + 'a'); @@ -27,7 +27,7 @@ describe('select tests', () => { it('should be iteratable multiple times', () => { const numbers = fromIterable([1, 2, 3, 4, 5, 6, 7]).select(_ => _ * 2); - expect(Array.from(numbers)).to.deep.equal([ 2, 4, 6, 8, 10, 12, 14 ]); - expect(Array.from(numbers)).to.deep.equal([ 2, 4, 6, 8, 10, 12, 14 ]); + expect(Array.from(numbers)).to.deep.equal([2, 4, 6, 8, 10, 12, 14]); + expect(Array.from(numbers)).to.deep.equal([2, 4, 6, 8, 10, 12, 14]); }); }); diff --git a/test/unit/single.spec.js b/test/unit/single.spec.js index c8c67de..7bcb85a 100644 --- a/test/unit/single.spec.js +++ b/test/unit/single.spec.js @@ -8,12 +8,16 @@ describe('single finalizer', () => { }); it('should throw exception if no values', () => { - const val = function () { return fromIterable([1, 2, 3, 4, 5]).where(_ => _ === 9).single(); }; + const val = function () { + return fromIterable([1, 2, 3, 4, 5]).where(_ => _ === 9).single(); + }; expect(val).to.throw(RangeError, 'Sequence does not contain single item'); }); it('should throw exception if multiple values', () => { - const val = function () { return fromIterable([1, 2, 3, 4, 5]).where(_ => _ === 1 || _ === 2).single(); }; + const val = function () { + return fromIterable([1, 2, 3, 4, 5]).where(_ => _ === 1 || _ === 2).single(); + }; expect(val).to.throw(RangeError, 'Sequence does not contain single item'); }); @@ -28,7 +32,9 @@ describe('single finalizer', () => { }); it('should singleOrDefault throw if multiple values ', () => { - const val = function () { return fromIterable([1, 2, 3, 4, 5]).where(_ => _ > 2).singleOrDefault(9); }; + const val = function () { + return fromIterable([1, 2, 3, 4, 5]).where(_ => _ > 2).singleOrDefault(9); + }; expect(val).to.throw(RangeError, 'Sequence contains multiple items'); }); diff --git a/test/unit/skip.spec.js b/test/unit/skip.spec.js index 695a550..fc8c5d7 100644 --- a/test/unit/skip.spec.js +++ b/test/unit/skip.spec.js @@ -2,36 +2,36 @@ import { expect } from 'chai'; import { fromIterable } from "../../src"; describe('skip tests', () => { - it ('should skip first n numbers', () => { + it('should skip first n numbers', () => { const output = fromIterable([1, 2, 3, 4, 5]).skip(2).toArray(); expect(output).to.deep.equal([3, 4, 5]); }); - it ('should skip first n numbers after another operation', () => { + it('should skip first n numbers after another operation', () => { const output = fromIterable([1, 2, 3, 4, 5]).where(_ => _ > 2).skip(2).toArray(); expect(output).to.deep.equal([5]); }); - it ('should skip be able to continue with another operator', () => { + it('should skip be able to continue with another operator', () => { const output = fromIterable([1, 2, 3, 4, 5]) - .where(_ => _ > 2) - .skip(1) - .select(x => `item_${x}`) - .toArray(); + .where(_ => _ > 2) + .skip(1) + .select(x => `item_${x}`) + .toArray(); expect(output).to.deep.equal(['item_4', 'item_5']); }); - it ('should skip return nothing if count is less', () => { + it('should skip return nothing if count is less', () => { const output = fromIterable([1, 2]) - .skip(3) - .toArray(); + .skip(3) + .toArray(); expect(output).to.deep.equal([]); }); - it ('should skip return empty from empty collection', () => { + it('should skip return empty from empty collection', () => { const output = fromIterable([]) - .skip(2) - .toArray(); + .skip(2) + .toArray(); expect(output).to.deep.equal([]); }); }); diff --git a/test/unit/take.spec.js b/test/unit/take.spec.js index ebc32ec..9e4a83b 100644 --- a/test/unit/take.spec.js +++ b/test/unit/take.spec.js @@ -2,36 +2,36 @@ import { expect } from 'chai'; import { fromIterable } from "../../src"; describe('take tests', () => { - it ('should take first n numbers', () => { + it('should take first n numbers', () => { const output = fromIterable([1, 2, 3, 4, 5]).take(3).toArray(); expect(output).to.deep.equal([1, 2, 3]); }); - it ('should take first n numbers after another operation', () => { + it('should take first n numbers after another operation', () => { const output = fromIterable([1, 2, 3, 4, 5]).where(_ => _ > 2).take(3).toArray(); expect(output).to.deep.equal([3, 4, 5]); }); - it ('should take be able to continue with another operator', () => { + it('should take be able to continue with another operator', () => { const output = fromIterable([1, 2, 3, 4, 5]) - .where(_ => _ > 2) - .take(3) - .select(x => `item_${x}`) - .toArray(); + .where(_ => _ > 2) + .take(3) + .select(x => `item_${x}`) + .toArray(); expect(output).to.deep.equal(['item_3', 'item_4', 'item_5']); }); - it ('should take all elements if source count is less', () => { + it('should take all elements if source count is less', () => { const output = fromIterable([1, 2]) - .take(3) - .toArray(); + .take(3) + .toArray(); expect(output).to.deep.equal([1, 2]); }); - it ('should take nothing from empty collection', () => { + it('should take nothing from empty collection', () => { const output = fromIterable([]) - .take(1) - .toArray(); + .take(1) + .toArray(); expect(output).to.deep.equal([]); }); }); diff --git a/test/unit/to.spec.js b/test/unit/to.spec.js index 43fc064..1f0cc13 100644 --- a/test/unit/to.spec.js +++ b/test/unit/to.spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { fromIterable, fromObject, range } from "../../src"; +import { fromIterable } from "../../src"; describe('output tests', () => { it('should create an array', () => { @@ -43,6 +43,6 @@ describe('output tests', () => { it('should create a set', () => { const set = fromIterable([1, 2, 3, 1, 2, 3]).toSet(); - expect(Array.from(set.values())).to.deep.equal([ 1, 2, 3 ]); + expect(Array.from(set.values())).to.deep.equal([1, 2, 3]); }); });