Showing preview only (589K chars total). Download the full file or copy to clipboard to get everything.
Repository: Siderite/LInQer
Branch: master
Commit: 28b91d808208
Files: 30
Total size: 570.3 KB
Directory structure:
gitextract_vvtgv5z_/
├── .gitignore
├── .vscode/
│ └── settings.json
├── LICENSE
├── LInQer.Enumerable.ts
├── LInQer.GroupEnumerable.ts
├── LInQer.OrderedEnumerable.ts
├── LInQer.Slim.ts
├── LInQer.all.d.ts
├── LInQer.all.js
├── LInQer.extra.js
├── LInQer.extra.ts
├── LInQer.js
├── LInQer.slim.js
├── README.md
├── Release.txt
├── build.bat
├── npm.export.ts
├── package.json
├── qUnit/
│ ├── qunit-2.9.2.css
│ └── qunit-2.9.2.js
├── tests.html
├── tests.js
├── tests.performance.html
├── tests.performance.js
├── tests.slim.html
├── tests.slim.js
├── tsconfig.all.json
├── tsconfig.extra.json
├── tsconfig.json
└── tsconfig.slim.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
================================================
FILE: .vscode/settings.json
================================================
{
"es6-css-minify.hideButton": "auto",
"es6-css-minify.js.mangle": true,
"es6-css-minify.js.compress": true
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Siderite
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: LInQer.Enumerable.ts
================================================
/// <reference path="./LInQer.Slim.ts" />
namespace Linqer {
export interface Enumerable extends Iterable<any> {
/**
* Applies an accumulator function over a sequence.
* The specified seed value is used as the initial accumulator value, and the specified function is used to select the result value.
*
* @param {*} accumulator
* @param {(acc: any, item: any) => any} aggregator
* @returns {*}
* @memberof Enumerable
*/
aggregate(accumulator: any, aggregator: (acc: any, item: any) => any): any;
/**
* Determines whether all elements of a sequence satisfy a condition.
* @param condition
* @returns true if all
*/
all(condition: IFilter): boolean;
/**
* Determines whether any element of a sequence exists or satisfies a condition.
*
* @param {IFilter} condition
* @returns {boolean}
* @memberof Enumerable
*/
any(condition: IFilter): boolean;
/**
* Appends a value to the end of the sequence.
*
* @param {*} item
* @returns {Enumerable}
* @memberof Enumerable
*/
append(item: any): Enumerable;
/**
* Computes the average of a sequence of numeric values.
*
* @returns {(number | undefined)}
* @memberof Enumerable
*/
average(): number | undefined;
/**
* Returns itself
*
* @returns {Enumerable}
* @memberof Enumerable
*/
asEnumerable(): Enumerable;
/**
* Checks the elements of a sequence based on their type
* If type is a string, it will check based on typeof, else it will use instanceof.
* Throws if types are different.
* @param {(string | Function)} type
* @returns {Enumerable}
* @memberof Enumerable
*/
cast(type: string | Function): Enumerable;
/**
* Determines whether a sequence contains a specified element.
* A custom function can be used to determine equality between elements.
*
* @param {*} item
* @param {IEqualityComparer} equalityComparer
* @returns {boolean}
* @memberof Enumerable
*/
contains(item: any, equalityComparer: IEqualityComparer): boolean;
defaultIfEmpty(): never;
/**
* Produces the set difference of two sequences
* WARNING: using the comparer is slower
*
* @param {IterableType} iterable
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
except(iterable: IterableType, equalityComparer: IEqualityComparer): Enumerable;
/**
* Produces the set intersection of two sequences.
* WARNING: using a comparer is slower
*
* @param {IterableType} iterable
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
intersect(iterable: IterableType, equalityComparer: IEqualityComparer): Enumerable;
/**
* Same as count
*
* @returns {number}
* @memberof Enumerable
*/
longCount(): number;
/**
* Filters the elements of a sequence based on their type
* If type is a string, it will filter based on typeof, else it will use instanceof
*
* @param {(string | Function)} type
* @returns {Enumerable}
* @memberof Enumerable
*/
ofType(type: string | Function): Enumerable;
/**
* Adds a value to the beginning of the sequence.
*
* @param {*} item
* @returns {Enumerable}
* @memberof Enumerable
*/
prepend(item: any): Enumerable;
/**
* Inverts the order of the elements in a sequence.
*
* @returns {Enumerable}
* @memberof Enumerable
*/
reverse(): Enumerable;
/**
* Projects each element of a sequence to an iterable and flattens the resulting sequences into one sequence.
*
* @param {ISelector<IterableType>} selector
* @returns {Enumerable}
* @memberof Enumerable
*/
selectMany(selector: ISelector<IterableType>): Enumerable;
/**
* Determines whether two sequences are equal and in the same order according to an optional equality comparer.
*
* @param {IterableType} iterable
* @param {IEqualityComparer} equalityComparer
* @returns {boolean}
* @memberof Enumerable
*/
sequenceEqual(iterable: IterableType, equalityComparer: IEqualityComparer): boolean;
/**
* Returns the single element of a sequence and throws if it doesn't have exactly one
*
* @returns {*}
* @memberof Enumerable
*/
single(): any;
/**
* Returns the single element of a sequence or undefined if none found. It throws if the sequence contains multiple items.
*
* @returns {(any | undefined)}
* @memberof Enumerable
*/
singleOrDefault(): any | undefined;
/**
* Returns a new enumerable collection that contains the elements from source with the last nr elements of the source collection omitted.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
skipLast(nr: number): Enumerable;
/**
* Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
*
* @param {IFilter} condition
* @returns {Enumerable}
* @memberof Enumerable
*/
skipWhile(condition: IFilter): Enumerable;
/**
* Selects the elements starting at the given start argument, and ends at, but does not include, the given end argument.
* @param start
* @param end
* @returns slice
*/
slice(start: number | undefined, end: number | undefined) : Enumerable;
/**
* Returns a new enumerable collection that contains the last nr elements from source.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
takeLast(nr: number): Enumerable;
/**
* Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements.
*
* @param {IFilter} condition
* @returns {Enumerable}
* @memberof Enumerable
*/
takeWhile(condition: IFilter): Enumerable;
toDictionary(): never;
/**
* creates a Map from an Enumerable
*
* @param {ISelector} keySelector
* @param {ISelector} valueSelector
* @returns {Map<any, any>}
* @memberof Enumerable
*/
toMap(keySelector: ISelector, valueSelector: ISelector): Map<any, any>;
/**
* creates an object from an Enumerable
*
* @param {ISelector} keySelector
* @param {ISelector} valueSelector
* @returns {{ [key: string]: any }}
* @memberof Enumerable
*/
toObject(keySelector: ISelector, valueSelector: ISelector): { [key: string]: any };
toHashSet(): never;
/**
* creates a Set from an enumerable
*
* @returns {Set<any>}
* @memberof Enumerable
*/
toSet(): Set<any>;
/**
* Produces the set union of two sequences.
*
* @param {IterableType} iterable
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
union(iterable: IterableType, equalityComparer: IEqualityComparer): Enumerable;
/**
* Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
*
* @param {IterableType} iterable
* @param {(item1: any, item2: any, index: number) => any} zipper
* @returns {*}
* @memberof Enumerable
*/
zip(iterable: IterableType, zipper: (item1: any, item2: any, index: number) => any): any;
}
/// Applies an accumulator function over a sequence.
/// The specified seed value is used as the initial accumulator value, and the specified function is used to select the result value.
Enumerable.prototype.aggregate = function (accumulator: any, aggregator: (acc: any, item: any) => any): any {
_ensureFunction(aggregator);
for (const item of this) {
accumulator = aggregator(accumulator, item);
}
return accumulator;
}
/// Determines whether all elements of a sequence satisfy a condition.
Enumerable.prototype.all = function (condition: IFilter): boolean {
_ensureFunction(condition);
return !this.any(x => !condition(x));
}
/// Determines whether any element of a sequence exists or satisfies a condition.
Enumerable.prototype.any = function (condition: IFilter): boolean {
_ensureFunction(condition);
let index = 0;
for (const item of this) {
if (condition(item, index)) return true;
index++;
}
return false;
}
/// Appends a value to the end of the sequence.
Enumerable.prototype.append = function (item: any): Enumerable {
return this.concat([item]);
}
/// Computes the average of a sequence of numeric values.
Enumerable.prototype.average = function (): number | undefined {
const stats = this.sumAndCount();
return stats.count === 0
? undefined
: stats.sum / stats.count;
}
/// Returns the same enumerable
Enumerable.prototype.asEnumerable = function (): Enumerable {
return this;
}
/// Checks the elements of a sequence based on their type
/// If type is a string, it will check based on typeof, else it will use instanceof.
/// Throws if types are different.
Enumerable.prototype.cast = function (type: string | Function): Enumerable {
const f: ((x: any) => boolean) = typeof type === 'string'
? x => typeof x === type
: x => x instanceof type;
return this.select(item => {
if (!f(item)) throw new Error(item + ' not of type ' + type);
return item;
});
}
/// Determines whether a sequence contains a specified element.
/// A custom function can be used to determine equality between elements.
Enumerable.prototype.contains = function (item: any, equalityComparer: IEqualityComparer = EqualityComparer.default): boolean {
_ensureFunction(equalityComparer);
return this.any(x => equalityComparer(x, item));
}
Enumerable.prototype.defaultIfEmpty = function (): never {
throw new Error('defaultIfEmpty not implemented for Javascript');
}
/// Produces the set difference of two sequences WARNING: using the comparer is slower
Enumerable.prototype.except = function (iterable: IterableType, equalityComparer: IEqualityComparer = EqualityComparer.default): Enumerable {
_ensureIterable(iterable);
const self: Enumerable = this;
// use a Set for performance if the comparer is not set
const gen = equalityComparer === EqualityComparer.default
? function* () {
const distinctValues = Enumerable.from(iterable).toSet();
for (const item of self) {
if (!distinctValues.has(item)) yield item;
}
}
// use exceptByHash from Linqer.extra for better performance
: function* () {
const values = _toArray(iterable);
for (const item of self) {
let unique = true;
for (let i=0; i<values.length; i++) {
if (equalityComparer(item, values[i])) {
unique = false;
break;
}
}
if (unique) yield item;
}
};
return new Enumerable(gen);
}
/// Produces the set intersection of two sequences. WARNING: using a comparer is slower
Enumerable.prototype.intersect = function (iterable: IterableType, equalityComparer: IEqualityComparer = EqualityComparer.default): Enumerable {
_ensureIterable(iterable);
const self: Enumerable = this;
// use a Set for performance if the comparer is not set
const gen = equalityComparer === EqualityComparer.default
? function* () {
const distinctValues = new Set(Enumerable.from(iterable));
for (const item of self) {
if (distinctValues.has(item)) yield item;
}
}
// use intersectByHash from Linqer.extra for better performance
: function* () {
const values = _toArray(iterable);
for (const item of self) {
let unique = true;
for (let i=0; i<values.length; i++) {
if (equalityComparer(item, values[i])) {
unique = false;
break;
}
}
if (!unique) yield item;
}
};
return new Enumerable(gen);
}
/// same as count
Enumerable.prototype.longCount = function (): number {
return this.count();
}
/// Filters the elements of a sequence based on their type
/// If type is a string, it will filter based on typeof, else it will use instanceof
Enumerable.prototype.ofType = function (type: string | Function): Enumerable {
const condition: IFilter =
typeof type === 'string'
? x => typeof x === type
: x => x instanceof type;
return this.where(condition);
}
/// Adds a value to the beginning of the sequence.
Enumerable.prototype.prepend = function (item: any): Enumerable {
return new Enumerable([item]).concat(this);
}
/// Inverts the order of the elements in a sequence.
Enumerable.prototype.reverse = function (): Enumerable {
_ensureInternalTryGetAt(this);
const self: Enumerable = this;
// if it can seek, just read the enumerable backwards
const gen = this._canSeek
? function* () {
const length = self.count();
for (let index = length - 1; index >= 0; index--) {
yield self.elementAt(index);
}
}
// else enumerate it all into an array, then read it backwards
: function* () {
const arr = self.toArray();
for (let index = arr.length - 1; index >= 0; index--) {
yield arr[index];
}
};
// the count is the same when reversed
const result = new Enumerable(gen);
_ensureInternalCount(this);
result._count = this._count;
_ensureInternalTryGetAt(this);
// have a custom indexer only if the original enumerable could seek
if (this._canSeek) {
const self = this;
result._canSeek = true;
result._tryGetAt = index => self._tryGetAt!(self.count() - index - 1);
}
return result;
}
/// Projects each element of a sequence to an iterable and flattens the resulting sequences into one sequence.
Enumerable.prototype.selectMany = function (selector: ISelector<IterableType>): Enumerable {
if (typeof selector !== 'undefined') {
_ensureFunction(selector);
} else {
selector = x => x;
}
const self: Enumerable = this;
const gen = function* () {
let index = 0;
for (const item of self) {
const iter = selector(item, index);
_ensureIterable(iter);
for (const child of iter) {
yield child;
}
index++;
}
};
return new Enumerable(gen);
}
/// Determines whether two sequences are equal and in the same order according to an equality comparer.
Enumerable.prototype.sequenceEqual = function (iterable: IterableType, equalityComparer: IEqualityComparer = EqualityComparer.default): boolean {
_ensureIterable(iterable);
_ensureFunction(equalityComparer);
const iterator1 = this[Symbol.iterator]();
const iterator2 = Enumerable.from(iterable)[Symbol.iterator]();
let done = false;
do {
const val1 = iterator1.next();
const val2 = iterator2.next();
const equal = (val1.done && val2.done) || (!val1.done && !val2.done && equalityComparer(val1.value, val2.value));
if (!equal) return false;
done = !!val1.done;
} while (!done);
return true;
}
/// Returns the single element of a sequence and throws if it doesn't have exactly one
Enumerable.prototype.single = function (): any {
const iterator = this[Symbol.iterator]();
let val = iterator.next();
if (val.done) throw new Error('Sequence contains no elements');
const result = val.value;
val = iterator.next();
if (!val.done) throw new Error('Sequence contains more than one element');
return result;
}
/// Returns the single element of a sequence or undefined if none found. It throws if the sequence contains multiple items.
Enumerable.prototype.singleOrDefault = function (): any | undefined {
const iterator = this[Symbol.iterator]();
let val = iterator.next();
if (val.done) return undefined;
const result = val.value;
val = iterator.next();
if (!val.done) throw new Error('Sequence contains more than one element');
return result;
}
/// Selects the elements starting at the given start argument, and ends at, but does not include, the given end argument.
Enumerable.prototype.slice = function (start: number = 0, end: number | undefined): Enumerable {
let enumerable: Enumerable = this;
// when the end is defined and positive and start is negative,
// the only way to compute the last index is to know the count
if (end !== undefined && end >= 0 && (start || 0) < 0) {
enumerable = enumerable.toList();
start = enumerable.count() + start;
}
if (start !== 0) {
if (start > 0) {
enumerable = enumerable.skip(start);
} else {
enumerable = enumerable.takeLast(-start);
}
}
if (end !== undefined) {
if (end >= 0) {
enumerable = enumerable.take(end - start);
} else {
enumerable = enumerable.skipLast(-end);
}
}
return enumerable;
}
/// Returns a new enumerable collection that contains the elements from source with the last nr elements of the source collection omitted.
Enumerable.prototype.skipLast = function (nr: number): Enumerable {
const self: Enumerable = this;
// the generator is using a buffer to cache nr values
// and only yields the values that overflow from it
const gen = function* () {
let nrLeft = nr;
const buffer = Array(nrLeft);
let index = 0;
let offset = 0;
for (const item of self) {
const value = buffer[index - offset];
buffer[index - offset] = item;
index++;
if (index - offset >= nrLeft) {
offset += nrLeft;
}
if (index > nrLeft) {
yield value;
}
}
buffer.length = 0;
};
const result = new Enumerable(gen);
// the count is the original count minus the skipped items and at least 0
result._count = () => Math.max(0, self.count() - nr);
_ensureInternalTryGetAt(this);
result._canSeek = this._canSeek;
// it has an indexer only if the original enumerable can seek
if (this._canSeek) {
result._tryGetAt = index => {
if (index >= result.count()) return null;
return self._tryGetAt!(index);
}
}
return result;
}
/// Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
Enumerable.prototype.skipWhile = function (condition: IFilter): Enumerable {
_ensureFunction(condition);
const self: Enumerable = this;
let skip = true;
const gen = function* () {
let index = 0;
for (const item of self) {
if (skip && !condition(item, index)) {
skip = false;
}
if (!skip) {
yield item;
}
index++;
}
};
return new Enumerable(gen);
}
/// Returns a new enumerable collection that contains the last nr elements from source.
Enumerable.prototype.takeLast = function (nr: number): Enumerable {
_ensureInternalTryGetAt(this);
const self: Enumerable = this;
const gen = this._canSeek
// taking the last items is easy if the enumerable can seek
? function* () {
let nrLeft = nr;
const length = self.count();
for (let index = length - nrLeft; index < length; index++) {
yield self.elementAt(index);
}
}
// else the generator uses a buffer to fill with values
// and yields them after the entire thing has been iterated
: function* () {
let nrLeft = nr;
let index = 0;
const buffer = Array(nrLeft);
for (const item of self) {
buffer[index % nrLeft] = item;
index++;
}
for (let i = 0; i < nrLeft && i < index; i++) {
yield buffer[(index + i) % nrLeft];
}
};
const result = new Enumerable(gen);
// the count is the minimum between nr and the enumerable count
result._count = () => Math.min(nr, self.count());
result._canSeek = self._canSeek;
// this can seek only if the original enumerable could seek
if (self._canSeek) {
result._tryGetAt = index => {
if (index < 0 || index >= result.count()) return null;
return self._tryGetAt!(self.count() - nr + index);
};
}
return result;
}
/// Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements.
Enumerable.prototype.takeWhile = function (condition: IFilter): Enumerable {
_ensureFunction(condition);
const self: Enumerable = this;
const gen = function* () {
let index = 0;
for (const item of self) {
if (condition(item, index)) {
yield item;
} else {
break;
}
index++;
}
};
return new Enumerable(gen);
}
Enumerable.prototype.toDictionary = function (): never {
throw new Error('use toMap or toObject instead of toDictionary');
}
/// creates a map from an Enumerable
Enumerable.prototype.toMap = function (keySelector: ISelector, valueSelector: ISelector = x => x): Map<any, any> {
_ensureFunction(keySelector);
_ensureFunction(valueSelector);
const result = new Map<any, any>();
let index = 0;
for (const item of this) {
result.set(keySelector(item, index), valueSelector(item, index));
index++;
}
return result;
}
/// creates an object from an enumerable
Enumerable.prototype.toObject = function (keySelector: ISelector, valueSelector: ISelector = x => x): { [key: string]: any } {
_ensureFunction(keySelector);
_ensureFunction(valueSelector);
const result: { [key: string]: any } = {};
let index = 0;
for (const item of this) {
result[keySelector(item, index)] = valueSelector(item);
index++;
}
return result;
}
Enumerable.prototype.toHashSet = function (): never {
throw new Error('use toSet instead of toHashSet');
}
/// creates a set from an enumerable
Enumerable.prototype.toSet = function (): Set<any> {
const result = new Set<any>();
for (const item of this) {
result.add(item);
}
return result;
}
/// Produces the set union of two sequences.
Enumerable.prototype.union = function (iterable: IterableType, equalityComparer: IEqualityComparer = EqualityComparer.default): Enumerable {
_ensureIterable(iterable);
return this.concat(iterable).distinct(equalityComparer);
}
/// Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
Enumerable.prototype.zip = function (iterable: IterableType, zipper: (item1: any, item2: any, index: number) => any): any {
_ensureIterable(iterable);
if (!zipper) {
zipper = (i1,i2)=>[i1,i2];
} else {
_ensureFunction(zipper);
}
const self: Enumerable = this;
const gen = function* () {
let index = 0;
const iterator1 = self[Symbol.iterator]();
const iterator2 = Enumerable.from(iterable)[Symbol.iterator]();
let done = false;
do {
const val1 = iterator1.next();
const val2 = iterator2.next();
done = !!(val1.done || val2.done);
if (!done) {
yield zipper(val1.value, val2.value, index);
}
index++;
} while (!done);
};
return new Enumerable(gen);
}
}
================================================
FILE: LInQer.GroupEnumerable.ts
================================================
/// <reference path="./LInQer.Slim.ts" />
namespace Linqer {
export interface Enumerable extends Iterable<any> {
/**
* Groups the elements of a sequence.
*
* @param {ISelector} keySelector
* @returns {Enumerable}
* @memberof Enumerable
*/
groupBy(keySelector: ISelector): Enumerable;
/**
* Correlates the elements of two sequences based on key equality and groups the results. A specified equalityComparer is used to compare keys.
* WARNING: using the equality comparer will be slower
*
* @param {IterableType} iterable
* @param {ISelector} innerKeySelector
* @param {ISelector} outerKeySelector
* @param {(item1: any, item2: any) => any} resultSelector
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
groupJoin(iterable: IterableType,
innerKeySelector: ISelector,
outerKeySelector: ISelector,
resultSelector: (item1: any, item2: any) => any,
equalityComparer: IEqualityComparer): Enumerable;
/**
* Correlates the elements of two sequences based on matching keys.
* WARNING: using the equality comparer will be slower
*
* @param {IterableType} iterable
* @param {ISelector} innerKeySelector
* @param {ISelector} outerKeySelector
* @param {(item1: any, item2: any) => any} resultSelector
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
join(iterable: IterableType,
innerKeySelector: ISelector,
outerKeySelector: ISelector,
resultSelector: (item1: any, item2: any) => any,
equalityComparer: IEqualityComparer): Enumerable;
toLookup(): never;
}
/// Groups the elements of a sequence.
Enumerable.prototype.groupBy = function (keySelector: ISelector): Enumerable {
_ensureFunction(keySelector);
const self: Enumerable = this;
const gen = function* () {
const groupMap = new Map<any, any>();
let index = 0;
// iterate all items and group them in a Map
for (const item of self) {
const key = keySelector(item, index);
const group = groupMap.get(key);
if (group) {
group.push(item);
} else {
groupMap.set(key, [item]);
}
index++;
}
// then yield a GroupEnumerable for each group
for (const [key, items] of groupMap) {
const group = new GroupEnumerable(items, key);
yield group;
}
};
const result = new Enumerable(gen);
return result;
}
/// Correlates the elements of two sequences based on key equality and groups the results. A specified equalityComparer is used to compare keys.
/// WARNING: using the equality comparer will be slower
Enumerable.prototype.groupJoin = function (iterable: IterableType,
innerKeySelector: ISelector,
outerKeySelector: ISelector,
resultSelector: (item1: any, item2: any) => any,
equalityComparer: IEqualityComparer = EqualityComparer.default): Enumerable {
const self: Enumerable = this;
const gen = equalityComparer === EqualityComparer.default
? function* () {
const lookup = new Enumerable(iterable)
.groupBy(outerKeySelector)
.toMap(g => g.key, g => g);
let index = 0;
for (const innerItem of self) {
const arr = _toArray(lookup.get(innerKeySelector(innerItem, index)));
yield resultSelector(innerItem, arr);
index++;
}
}
: function* () {
let innerIndex = 0;
for (const innerItem of self) {
const arr = [];
let outerIndex = 0;
for (const outerItem of Enumerable.from(iterable)) {
if (equalityComparer(innerKeySelector(innerItem, innerIndex), outerKeySelector(outerItem, outerIndex))) {
arr.push(outerItem);
}
outerIndex++;
}
yield resultSelector(innerItem, arr);
innerIndex++;
}
};
return new Enumerable(gen);
}
/// Correlates the elements of two sequences based on matching keys.
/// WARNING: using the equality comparer will be slower
Enumerable.prototype.join = function (iterable: IterableType,
innerKeySelector: ISelector,
outerKeySelector: ISelector,
resultSelector: (item1: any, item2: any) => any,
equalityComparer: IEqualityComparer = EqualityComparer.default): Enumerable {
const self: Enumerable = this;
const gen = equalityComparer === EqualityComparer.default
? function* () {
const lookup = new Enumerable(iterable)
.groupBy(outerKeySelector)
.toMap(g => g.key, g => g);
let index = 0;
for (const innerItem of self) {
const group = lookup.get(innerKeySelector(innerItem, index));
if (group) {
for (const outerItem of group) {
yield resultSelector(innerItem, outerItem);
}
}
index++;
}
}
: function* () {
let innerIndex = 0;
for (const innerItem of self) {
let outerIndex = 0;
for (const outerItem of Enumerable.from(iterable)) {
if (equalityComparer(innerKeySelector(innerItem, innerIndex), outerKeySelector(outerItem, outerIndex))) {
yield resultSelector(innerItem, outerItem);
}
outerIndex++;
}
innerIndex++;
}
};
return new Enumerable(gen);
}
Enumerable.prototype.toLookup = function (): never {
throw new Error('use groupBy instead of toLookup');
}
/**
* An Enumerable that also exposes a group key
*
* @export
* @class GroupEnumerable
* @extends {Enumerable}
*/
export class GroupEnumerable extends Enumerable {
key: string;
constructor(iterable: IterableType, key: string) {
super(iterable);
this.key = key;
}
}
}
================================================
FILE: LInQer.OrderedEnumerable.ts
================================================
/// <reference path="./LInQer.Slim.ts" />
namespace Linqer {
export interface Enumerable extends Iterable<any> {
/**
* Sorts the elements of a sequence in ascending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof Enumerable
*/
orderBy(keySelector: ISelector): OrderedEnumerable;
/**
* Sorts the elements of a sequence in descending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof Enumerable
*/
orderByDescending(keySelector: ISelector): OrderedEnumerable;
/**
* use QuickSort for ordering (default). Recommended when take, skip, takeLast, skipLast are used after orderBy
*
* @returns {Enumerable}
* @memberof Enumerable
*/
useQuickSort(): Enumerable;
/**
* use the default browser sort implementation for ordering at all times
*
* @returns {Enumerable}
* @memberof Enumerable
*/
useBrowserSort(): Enumerable;
}
/// Sorts the elements of a sequence in ascending order.
Enumerable.prototype.orderBy = function (keySelector: ISelector): OrderedEnumerable {
if (keySelector) {
_ensureFunction(keySelector);
} else {
keySelector = item => item;
}
return new OrderedEnumerable(this, keySelector, true);
};
/// Sorts the elements of a sequence in descending order.
Enumerable.prototype.orderByDescending = function (keySelector: ISelector): OrderedEnumerable {
if (keySelector) {
_ensureFunction(keySelector);
} else {
keySelector = item => item;
}
return new OrderedEnumerable(this, keySelector, false);
};
/// use QuickSort for ordering (default). Recommended when take, skip, takeLast, skipLast are used after orderBy
Enumerable.prototype.useQuickSort = function (): Enumerable {
this._useQuickSort = true;
return this;
};
/// use the default browser sort implementation for ordering at all times
Enumerable.prototype.useBrowserSort = function (): Enumerable {
this._useQuickSort = false;
return this;
};
//static sort: (arr: any[], comparer?: IComparer) => void;
Enumerable.sort = function (arr: any[], comparer: IComparer = _defaultComparer): any[] {
_quickSort(arr, 0, arr.length - 1, comparer, 0, Number.MAX_SAFE_INTEGER);
return arr;
}
enum RestrictionType {
skip,
skipLast,
take,
takeLast
}
/**
* An Enumerable yielding ordered items
*
* @export
* @class OrderedEnumerable
* @extends {Enumerable}
*/
export class OrderedEnumerable extends Enumerable {
_keySelectors: { keySelector: ISelector, ascending: boolean }[];
_restrictions: { type: RestrictionType, nr: number }[];
/**
*Creates an instance of OrderedEnumerable.
* @param {IterableType} src
* @param {ISelector} [keySelector]
* @param {boolean} [ascending=true]
* @memberof OrderedEnumerable
*/
constructor(src: IterableType,
keySelector?: ISelector,
ascending: boolean = true) {
super(src);
this._keySelectors = [];
this._restrictions = [];
if (keySelector) {
this._keySelectors.push({ keySelector: keySelector, ascending: ascending });
}
const self: OrderedEnumerable = this;
// generator gets an array of the original,
// sorted inside the interval determined by functions such as skip, take, skipLast, takeLast
this._generator = function* () {
let { startIndex, endIndex, arr } = this.getSortedArray();
if (arr) {
for (let index = startIndex; index < endIndex; index++) {
yield arr[index];
}
}
};
// the count is the difference between the end and start indexes
// if no skip/take functions were used, this will be the original count
this._count = () => {
const totalCount = Enumerable.from(self._src).count();
const { startIndex, endIndex } = this.getStartAndEndIndexes(self._restrictions, totalCount);
return endIndex - startIndex;
};
// an ordered enumerable cannot seek
this._canSeek=false;
this._tryGetAt = ()=>{ throw new Error('Ordered enumerables cannot seek'); };
}
private getSortedArray() {
const self = this;
let startIndex: number;
let endIndex: number;
let arr: any[] | null = null;
const innerEnumerable = self._src as Enumerable;
_ensureInternalTryGetAt(innerEnumerable);
// try to avoid enumerating the entire original into an array
if (innerEnumerable._canSeek) {
({ startIndex, endIndex } = self.getStartAndEndIndexes(self._restrictions, innerEnumerable.count()));
} else {
arr = Array.from(self._src);
({ startIndex, endIndex } = self.getStartAndEndIndexes(self._restrictions, arr.length));
}
if (startIndex < endIndex) {
if (!arr) {
arr = Array.from(self._src);
}
// only quicksort supports partial ordering inside an interval
const sort: (item1: any, item2: any) => void = self._useQuickSort
? (a, c) => _quickSort(a, 0, a.length - 1, c, startIndex, endIndex)
: (a, c) => a.sort(c);
const sortFunc = self.generateSortFunc(self._keySelectors);
sort(arr, sortFunc);
return {
startIndex,
endIndex,
arr
};
} else {
return {
startIndex,
endIndex,
arr: null
};
}
}
private generateSortFunc(selectors: { keySelector: ISelector, ascending: boolean }[]): (i1: any, i2: any) => number {
// simplify the selectors into an array of comparers
const comparers = selectors.map(s => {
const f = s.keySelector;
const comparer = (i1: any, i2: any) => {
const k1 = f(i1);
const k2 = f(i2);
if (k1 > k2) return 1;
if (k1 < k2) return -1;
return 0;
};
return s.ascending
? comparer
: (i1: any, i2: any) => -comparer(i1, i2);
});
// optimize the resulting sort function in the most common case
// (ordered by a single criterion)
return comparers.length == 1
? comparers[0]
: (i1: any, i2: any) => {
for (let i = 0; i < comparers.length; i++) {
const v = comparers[i](i1, i2);
if (v) return v;
}
return 0;
};
}
/// calculate the interval in which an array needs to have ordered items for this ordered enumerable
private getStartAndEndIndexes(restrictions: { type: RestrictionType, nr: number }[], arrLength: number) {
let startIndex = 0;
let endIndex = arrLength;
for (const restriction of restrictions) {
switch (restriction.type) {
case RestrictionType.take:
endIndex = Math.min(endIndex, startIndex + restriction.nr);
break;
case RestrictionType.skip:
startIndex = Math.min(endIndex, startIndex + restriction.nr);
break;
case RestrictionType.takeLast:
startIndex = Math.max(startIndex, endIndex - restriction.nr);
break;
case RestrictionType.skipLast:
endIndex = Math.max(startIndex, endIndex - restriction.nr);
break;
}
}
return { startIndex, endIndex };
}
/**
* Performs a subsequent ordering of the elements in a sequence in ascending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
thenBy(keySelector: ISelector): OrderedEnumerable {
this._keySelectors.push({ keySelector: keySelector, ascending: true });
return this;
}
/**
* Performs a subsequent ordering of the elements in a sequence in descending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
thenByDescending(keySelector: ISelector): OrderedEnumerable {
this._keySelectors.push({ keySelector: keySelector, ascending: false });
return this;
}
/**
* Deferred and optimized implementation of take
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
take(nr: number): OrderedEnumerable {
this._restrictions.push({ type: RestrictionType.take, nr: nr });
return this;
}
/**
* Deferred and optimized implementation of takeLast
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
takeLast(nr: number): OrderedEnumerable {
this._restrictions.push({ type: RestrictionType.takeLast, nr: nr });
return this;
}
/**
* Deferred and optimized implementation of skip
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
skip(nr: number): OrderedEnumerable {
this._restrictions.push({ type: RestrictionType.skip, nr: nr });
return this;
}
/**
* Deferred and optimized implementation of skipLast
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
skipLast(nr: number): OrderedEnumerable {
this._restrictions.push({ type: RestrictionType.skipLast, nr: nr });
return this;
}
/**
* An optimized implementation of toArray
*
* @returns {any[]}
* @memberof OrderedEnumerable
*/
toArray(): any[] {
const { startIndex, endIndex, arr } = this.getSortedArray();
return arr
? arr.slice(startIndex, endIndex)
: [];
}
/**
* An optimized implementation of toMap
*
* @param {ISelector} keySelector
* @param {ISelector} [valueSelector=x => x]
* @returns {Map<any, any>}
* @memberof OrderedEnumerable
*/
toMap(keySelector: ISelector, valueSelector: ISelector = x => x): Map<any, any> {
_ensureFunction(keySelector);
_ensureFunction(valueSelector);
const result = new Map<any, any>();
const arr = this.toArray();
for (let i = 0; i < arr.length; i++) {
result.set(keySelector(arr[i], i), valueSelector(arr[i], i));
}
return result;
}
/**
* An optimized implementation of toObject
*
* @param {ISelector} keySelector
* @param {ISelector} [valueSelector=x => x]
* @returns {{ [key: string]: any }}
* @memberof OrderedEnumerable
*/
toObject(keySelector: ISelector, valueSelector: ISelector = x => x): { [key: string]: any } {
_ensureFunction(keySelector);
_ensureFunction(valueSelector);
const result: { [key: string]: any } = {};
const arr = this.toArray();
for (let i = 0; i < arr.length; i++) {
result[keySelector(arr[i], i)] = valueSelector(arr[i], i);
}
return result;
}
/**
* An optimized implementation of to Set
*
* @returns {Set<any>}
* @memberof OrderedEnumerable
*/
toSet(): Set<any> {
const result = new Set<any>();
const arr = this.toArray();
for (let i = 0; i < arr.length; i++) {
result.add(arr[i]);
}
return result;
}
}
const _insertionSortThreshold = 64;
/// insertion sort is used for small intervals
function _insertionsort(arr: any[], leftIndex: number, rightIndex: number, comparer: IComparer) {
for (let j = leftIndex; j <= rightIndex; j++) {
const key = arr[j];
let i = j - 1;
while (i >= leftIndex && comparer(arr[i], key) > 0) {
arr[i + 1] = arr[i];
i--;
}
arr[i + 1] = key;
}
}
/// swap two items in an array by index
function _swapArrayItems(array: any[], leftIndex: number, rightIndex: number): void {
const temp = array[leftIndex];
array[leftIndex] = array[rightIndex];
array[rightIndex] = temp;
}
// Quicksort partition by center value coming from both sides
function _partition(items: any[], left: number, right: number, comparer: IComparer) {
const pivot = items[(right + left) >> 1];
while (left <= right) {
while (comparer(items[left], pivot) < 0) {
left++;
}
while (comparer(items[right], pivot) > 0) {
right--;
}
if (left < right) {
_swapArrayItems(items, left, right);
left++;
right--;
} else {
if (left === right) return left + 1;
}
}
return left;
}
/// optimized Quicksort algorithm
function _quickSort(items: any[], left: number, right: number, comparer: IComparer = _defaultComparer, minIndex: number = 0, maxIndex: number = Number.MAX_SAFE_INTEGER) {
if (!items.length) return items;
// store partition indexes to be processed in here
const partitions: { left: number, right: number }[] = [];
partitions.push({ left, right });
let size = 1;
// the actual size of the partitions array never decreases
// but we keep score of the number of partitions in 'size'
// and we reuse slots whenever possible
while (size) {
const partition = { left, right } = partitions[size-1];
if (right - left < _insertionSortThreshold) {
_insertionsort(items, left, right, comparer);
size--;
continue;
}
const index = _partition(items, left, right, comparer);
if (left < index - 1 && index - 1 >= minIndex) {
partition.right = index - 1;
if (index < right && index < maxIndex) {
partitions[size]={ left: index, right };
size++;
}
} else {
if (index < right && index < maxIndex) {
partition.left = index;
} else {
size--;
}
}
}
return items;
}
}
================================================
FILE: LInQer.Slim.ts
================================================
namespace Linqer {
/**
* wrapper class over iterable instances that exposes the methods usually found in .NET LINQ
*
* @export
* @class Enumerable
* @implements {Iterable<any>}
* @implements {IUsesQuickSort}
*/
export class Enumerable implements Iterable<any>, IUsesQuickSort {
_src: IterableType;
_generator: () => Iterator<any>;
_useQuickSort: boolean;
// indicates that count and elementAt functions will not cause iterating the enumerable
_canSeek: boolean;
_count: null | (() => number);
_tryGetAt: null | ((index: number) => { value: any } | null);
// true if the enumerable was iterated at least once
_wasIterated: boolean;
/**
* sort an array in place using the Enumerable sort algorithm (Quicksort)
*
* @static
* @memberof Enumerable
*/
static sort: (arr: any[], comparer?: IComparer) => any[];
/**
* You should never use this. Instead use Enumerable.from
* @param {IterableType} src
* @memberof Enumerable
*/
constructor(src: IterableType) {
_ensureIterable(src);
this._src = src;
const iteratorFunction: (() => Iterator<any>) = (src as Iterable<any>)[Symbol.iterator];
// the generator is either the iterator of the source enumerable
// or the generator function that was provided as the source itself
if (iteratorFunction) {
this._generator = iteratorFunction.bind(src);
} else {
this._generator = src as (() => Iterator<any>);
}
// set sorting method on an enumerable and all the derived ones should inherit it
// TODO: a better method of doing this
this._useQuickSort = (src as IUsesQuickSort)._useQuickSort !== undefined
? (src as IUsesQuickSort)._useQuickSort
: true;
this._canSeek = false;
this._count = null;
this._tryGetAt = null;
this._wasIterated = false;
}
/**
* Wraps an iterable item into an Enumerable if it's not already one
*
* @static
* @param {IterableType} iterable
* @returns {Enumerable}
* @memberof Enumerable
*/
static from(iterable: IterableType): Enumerable {
if (iterable instanceof Enumerable) return iterable;
return new Enumerable(iterable);
}
/**
* the Enumerable instance exposes the same iterator as the wrapped iterable or generator function
*
* @returns {Iterator<any>}
* @memberof Enumerable
*/
[Symbol.iterator](): Iterator<any> {
this._wasIterated = true;
return this._generator();
}
/**
* returns an empty Enumerable
*
* @static
* @returns {Enumerable}
* @memberof Enumerable
*/
static empty(): Enumerable {
const result = new Enumerable([]);
result._count = () => 0;
result._tryGetAt = (index: number) => null;
result._canSeek = true;
return result;
}
/**
* generates a sequence of integer numbers within a specified range.
*
* @static
* @param {number} start
* @param {number} count
* @returns {Enumerable}
* @memberof Enumerable
*/
static range(start: number, count: number): Enumerable {
const gen = function* () {
for (let i = 0; i < count; i++) {
yield start + i;
}
};
const result = new Enumerable(gen);
result._count = () => count;
result._tryGetAt = index => {
if (index >= 0 && index < count) return { value: start + index };
return null;
};
result._canSeek = true;
return result;
}
/**
* Generates a sequence that contains one repeated value.
*
* @static
* @param {*} item
* @param {number} count
* @returns {Enumerable}
* @memberof Enumerable
*/
static repeat(item: any, count: number): Enumerable {
const gen = function* () {
for (let i = 0; i < count; i++) {
yield item;
}
};
const result = new Enumerable(gen);
result._count = () => count;
result._tryGetAt = index => {
if (index >= 0 && index < count) return { value: item };
return null;
};
result._canSeek = true;
return result;
}
/**
* Same value as count(), but will throw an Error if enumerable is not seekable and has to be iterated to get the length
*/
get length():number {
_ensureInternalTryGetAt(this);
if (!this._canSeek) throw new Error('Calling length on this enumerable will iterate it. Use count()');
return this.count();
}
/**
* Concatenates two sequences by appending iterable to the existing one.
*
* @param {IterableType} iterable
* @returns {Enumerable}
* @memberof Enumerable
*/
concat(iterable: IterableType): Enumerable {
_ensureIterable(iterable);
const self: Enumerable = this;
// the generator will iterate the enumerable first, then the iterable that was given as a parameter
// this will be able to seek if both the original and the iterable derived enumerable can seek
// the indexing function will get items from the first and then second enumerable without iteration
const gen = function* () {
for (const item of self) {
yield item;
}
for (const item of Enumerable.from(iterable)) {
yield item;
}
};
const result = new Enumerable(gen);
const other = Enumerable.from(iterable);
result._count = () => self.count() + other.count();
_ensureInternalTryGetAt(this);
_ensureInternalTryGetAt(other);
result._canSeek = self._canSeek && other._canSeek;
if (self._canSeek) {
result._tryGetAt = index => {
return self._tryGetAt!(index) || other._tryGetAt!(index - self.count());
};
}
return result;
}
/**
* Returns the number of elements in a sequence.
*
* @returns {number}
* @memberof Enumerable
*/
count(): number {
_ensureInternalCount(this);
return this._count!();
}
/**
* Returns distinct elements from a sequence.
* WARNING: using a comparer makes this slower. Not specifying it uses a Set to determine distinctiveness.
*
* @param {IEqualityComparer} [equalityComparer=EqualityComparer.default]
* @returns {Enumerable}
* @memberof Enumerable
*/
distinct(equalityComparer: IEqualityComparer = EqualityComparer.default): Enumerable {
const self: Enumerable = this;
// if the comparer function is not provided, a Set will be used to quickly determine distinctiveness
const gen = equalityComparer === EqualityComparer.default
? function* () {
const distinctValues = new Set();
for (const item of self) {
const size = distinctValues.size;
distinctValues.add(item);
if (size < distinctValues.size) {
yield item;
}
}
}
// otherwise values will be compared with previous values ( O(n^2) )
// use distinctByHash in Linqer.extra to use a hashing function ( O(n log n) )
: function* () {
const values = [];
for (const item of self) {
let unique = true;
for (let i=0; i<values.length; i++) {
if (equalityComparer(item, values[i])) {
unique = false;
break;
}
}
if (unique) yield item;
values.push(item);
}
};
return new Enumerable(gen);
}
/**
* Returns the element at a specified index in a sequence.
*
* @param {number} index
* @returns {*}
* @memberof Enumerable
*/
elementAt(index: number): any {
_ensureInternalTryGetAt(this);
const result = this._tryGetAt!(index);
if (!result) throw new Error('Index out of range');
return result.value;
}
/**
* Returns the element at a specified index in a sequence or undefined if the index is out of range.
*
* @param {number} index
* @returns {(any | undefined)}
* @memberof Enumerable
*/
elementAtOrDefault(index: number): any | undefined {
_ensureInternalTryGetAt(this);
const result = this._tryGetAt!(index);
if (!result) return undefined;
return result.value;
}
/**
* Returns the first element of a sequence.
*
* @returns {*}
* @memberof Enumerable
*/
first(): any {
return this.elementAt(0);
}
/**
* Returns the first element of a sequence, or a default value if no element is found.
*
* @returns {(any | undefined)}
* @memberof Enumerable
*/
firstOrDefault(): any | undefined {
return this.elementAtOrDefault(0);
}
/**
* Returns the last element of a sequence.
*
* @returns {*}
* @memberof Enumerable
*/
last(): any {
_ensureInternalTryGetAt(this);
// if this cannot seek, getting the last element requires iterating the whole thing
if (!this._canSeek) {
let result = null;
let found = false;
for (const item of this) {
result = item;
found = true;
}
if (found) return result;
throw new Error('The enumeration is empty');
}
// if this can seek, then just go directly at the last element
const count = this.count();
return this.elementAt(count - 1);
}
/**
* Returns the last element of a sequence, or undefined if no element is found.
*
* @returns {(any | undefined)}
* @memberof Enumerable
*/
lastOrDefault(): any | undefined {
_ensureInternalTryGetAt(this);
if (!this._canSeek) {
let result = undefined;
for (const item of this) {
result = item;
}
return result;
}
const count = this.count();
return this.elementAtOrDefault(count - 1);
}
/**
* Returns the count, minimum and maximum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {{ count: number, min: any, max: any }}
* @memberof Enumerable
*/
stats(comparer?: IComparer): { count: number, min: any, max: any } {
if (comparer) {
_ensureFunction(comparer);
} else {
comparer = _defaultComparer;
}
const agg = {
count: 0,
min: undefined,
max: undefined
};
for (const item of this) {
if (typeof agg.min === 'undefined' || comparer(item, agg.min) < 0) agg.min = item;
if (typeof agg.max === 'undefined' || comparer(item, agg.max) > 0) agg.max = item;
agg.count++;
}
return agg;
}
/**
* Returns the minimum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {*}
* @memberof Enumerable
*/
min(comparer?: IComparer): any {
const stats = this.stats(comparer);
return stats.count === 0
? undefined
: stats.min;
}
/**
* Returns the maximum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {*}
* @memberof Enumerable
*/
max(comparer?: IComparer): any {
const stats = this.stats(comparer);
return stats.count === 0
? undefined
: stats.max;
}
/**
* Projects each element of a sequence into a new form.
*
* @param {ISelector} selector
* @returns {Enumerable}
* @memberof Enumerable
*/
select(selector: ISelector): Enumerable {
_ensureFunction(selector);
const self: Enumerable = this;
// the generator is applying the selector on all the items of the enumerable
// the count of the resulting enumerable is the same as the original's
// the indexer is the same as that of the original, with the selector applied on the value
const gen = function* () {
let index = 0;
for (const item of self) {
yield selector(item, index);
index++;
}
};
const result = new Enumerable(gen);
_ensureInternalCount(this);
result._count = this._count;
_ensureInternalTryGetAt(self);
result._canSeek = self._canSeek;
result._tryGetAt = index => {
const res = self._tryGetAt!(index);
if (!res) return res;
return { value: selector(res.value) };
};
return result;
}
/**
* Bypasses a specified number of elements in a sequence and then returns the remaining elements.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
skip(nr: number): Enumerable {
const self: Enumerable = this;
// the generator just enumerates the first nr numbers then starts yielding values
// the count is the same as the original enumerable, minus the skipped items and at least 0
// the indexer is the same as for the original, with an offset
const gen = function* () {
let nrLeft = nr;
for (const item of self) {
if (nrLeft > 0) {
nrLeft--;
} else {
yield item;
}
}
};
const result = new Enumerable(gen);
result._count = () => Math.max(0, self.count() - nr);
_ensureInternalTryGetAt(this);
result._canSeek = this._canSeek;
result._tryGetAt = index => self._tryGetAt!(index + nr);
return result;
}
/**
* Takes start elements, ignores howmany elements, continues with the new items and continues with the original enumerable
* Equivalent to the value of an array after performing splice on it with the same parameters
* @param start
* @param howmany
* @param items
* @returns splice
*/
splice(start: number, howmany: number, ...newItems:any[]) : Enumerable {
// tried to define length and splice so that this is seen as an Array-like object,
// but it doesn't work on properties. length needs to be a field.
return this.take(start).concat(newItems).concat(this.skip(start+howmany));
}
/**
* Computes the sum of a sequence of numeric values.
*
* @returns {(number | undefined)}
* @memberof Enumerable
*/
sum(): number | undefined {
const stats = this.sumAndCount();
return stats.count === 0
? undefined
: stats.sum;
}
/**
* Computes the sum and count of a sequence of numeric values.
*
* @returns {{ sum: number, count: number }}
* @memberof Enumerable
*/
sumAndCount(): { sum: number, count: number } {
const agg = {
count: 0,
sum: 0
};
for (const item of this) {
agg.sum = agg.count === 0
? _toNumber(item)
: agg.sum + _toNumber(item);
agg.count++;
}
return agg;
}
/**
* Returns a specified number of contiguous elements from the start of a sequence.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
take(nr: number): Enumerable {
const self: Enumerable = this;
// the generator will stop after nr items yielded
// the count is the maximum between the total count and nr
// the indexer is the same, as long as it's not higher than nr
const gen = function* () {
let nrLeft = nr;
for (const item of self) {
if (nrLeft > 0) {
yield item;
nrLeft--;
}
if (nrLeft <= 0) {
break;
}
}
};
const result = new Enumerable(gen);
result._count = () => Math.min(nr, self.count());
_ensureInternalTryGetAt(this);
result._canSeek = self._canSeek;
if (self._canSeek) {
result._tryGetAt = index => {
if (index >= nr) return null;
return self._tryGetAt!(index);
};
}
return result;
}
/**
* creates an array from an Enumerable
*
* @returns {any[]}
* @memberof Enumerable
*/
toArray(): any[] {
_ensureInternalTryGetAt(this);
// this should be faster than Array.from(this)
if (this._canSeek) {
const arr = new Array(this.count());
for (let i = 0; i < arr.length; i++) {
arr[i] = this._tryGetAt!(i)?.value;
}
return arr;
}
// try to optimize the array growth by increasing it
// by 64 every time it is needed
const minIncrease = 64;
let size = 0;
const arr = [];
for (const item of this) {
if (size === arr.length) {
arr.length += minIncrease;
}
arr[size] = item;
size++;
}
arr.length = size;
return arr;
}
/**
* similar to toArray, but returns a seekable Enumerable (itself if already seekable) that can do count and elementAt without iterating
*
* @returns {Enumerable}
* @memberof Enumerable
*/
toList(): Enumerable {
_ensureInternalTryGetAt(this);
if (this._canSeek) return this;
return Enumerable.from(this.toArray());
}
/**
* Filters a sequence of values based on a predicate.
*
* @param {IFilter} condition
* @returns {Enumerable}
* @memberof Enumerable
*/
where(condition: IFilter): Enumerable {
_ensureFunction(condition);
const self: Enumerable = this;
// cannot imply the count or indexer from the condition
// where will have to iterate through the whole thing
const gen = function* () {
let index = 0;
for (const item of self) {
if (condition(item, index)) {
yield item;
}
index++;
}
};
return new Enumerable(gen);
}
}
// throw if src is not a generator function or an iteratable
export function _ensureIterable(src: IterableType): void {
if (src) {
if ((src as Iterable<any>)[Symbol.iterator]) return;
if (typeof src === 'function' && (src as Function).constructor.name === 'GeneratorFunction') return;
}
throw new Error('the argument must be iterable!');
}
// throw if f is not a function
export function _ensureFunction(f: Function): void {
if (!f || typeof f !== 'function') throw new Error('the argument needs to be a function!');
}
// return Nan if this is not a number
// different from Number(obj), which would cast strings to numbers
function _toNumber(obj: any): number {
return typeof obj === 'number'
? obj
: Number.NaN;
}
// return the iterable if already an array or use Array.from to create one
export function _toArray(iterable: IterableType) {
if (!iterable) return [];
if (Array.isArray(iterable)) return iterable;
return Array.from(iterable);
}
// if the internal count function is not defined, set it to the most appropriate one
export function _ensureInternalCount(enumerable: Enumerable) {
if (enumerable._count) return;
if (enumerable._src instanceof Enumerable) {
// the count is the same as the underlying enumerable
const innerEnumerable = enumerable._src as Enumerable;
_ensureInternalCount(innerEnumerable);
enumerable._count = () => innerEnumerable._count!();
return;
}
const src = enumerable._src as any;
// this could cause false positives, but if it has a numeric length or size, use it
if (typeof src !== 'function' && typeof src.length === 'number') {
enumerable._count = () => src.length;
return;
}
if (typeof src.size === 'number') {
enumerable._count = () => src.size;
return;
}
// otherwise iterate the whole thing and count all items
enumerable._count = () => {
let x = 0;
for (const item of enumerable) x++;
return x;
};
}
// ensure there is an internal indexer function adequate for this enumerable
// this also determines if the enumerable can seek
export function _ensureInternalTryGetAt(enumerable: Enumerable) {
if (enumerable._tryGetAt) return;
enumerable._canSeek = true;
if (enumerable._src instanceof Enumerable) {
// indexer and seekability is the same as for the underlying enumerable
const innerEnumerable = enumerable._src as Enumerable;
_ensureInternalTryGetAt(innerEnumerable);
enumerable._tryGetAt = index => innerEnumerable._tryGetAt!(index);
enumerable._canSeek = innerEnumerable._canSeek;
return;
}
if (typeof enumerable._src === 'string') {
// a string can be accessed by index
enumerable._tryGetAt = index => {
if (index < (enumerable._src as string).length) {
return { value: (enumerable._src as string).charAt(index) };
}
return null;
};
return;
}
if (Array.isArray(enumerable._src)) {
// an array can be accessed by index
enumerable._tryGetAt = index => {
if (index >= 0 && index < (enumerable._src as any[]).length) {
return { value: (enumerable._src as any[])[index] };
}
return null;
};
return;
}
const src = enumerable._src as any;
if (typeof enumerable._src !== 'function' && typeof src.length === 'number') {
// try to access an object with a defined numeric length by indexing it
// might cause false positives
enumerable._tryGetAt = index => {
if (index < src.length && typeof src[index] !== 'undefined') {
return { value: src[index] };
}
return null;
};
return;
}
enumerable._canSeek = false;
// TODO other specialized types? objects, maps, sets?
enumerable._tryGetAt = index => {
let x = 0;
for (const item of enumerable) {
if (index === x) return { value: item };
x++;
}
return null;
}
}
/**
* an extended iterable type that also supports generator functions
*/
export type IterableType = Iterable<any> | (() => Iterator<any>) | Enumerable;
/**
* A comparer function to be used in sorting
*/
export type IComparer = (item1: any, item2: any) => -1 | 0 | 1;
/**
* A selector function to be used in mapping
*/
export type ISelector<T = any> = (item: any, index?: number) => T;
/**
* A filter function
*/
export type IFilter = ISelector<boolean>;
/**
* The default comparer function between two items
* @param item1
* @param item2
*/
export const _defaultComparer: IComparer = (item1, item2) => {
if (item1 > item2) return 1;
if (item1 < item2) return -1;
return 0;
};
/**
* Interface for an equality comparer
*/
export type IEqualityComparer = (item1: any, item2: any) => boolean;
/**
* Predefined equality comparers
* default is the equivalent of ==
* exact is the equivalent of ===
*/
export const EqualityComparer = {
default: (item1: any, item2: any) => item1 == item2,
exact: (item1: any, item2: any) => item1 === item2,
};
// used to access the variable determining if
// an enumerable should be ordered using Quicksort or not
interface IUsesQuickSort {
_useQuickSort: boolean;
}
}
================================================
FILE: LInQer.all.d.ts
================================================
declare namespace Linqer {
/**
* wrapper class over iterable instances that exposes the methods usually found in .NET LINQ
*
* @export
* @class Enumerable
* @implements {Iterable<any>}
* @implements {IUsesQuickSort}
*/
export class Enumerable implements Iterable<any>, IUsesQuickSort {
_src: IterableType;
_generator: () => Iterator<any>;
_useQuickSort: boolean;
_canSeek: boolean;
_count: null | (() => number);
_tryGetAt: null | ((index: number) => {
value: any;
} | null);
_wasIterated: boolean;
/**
* sort an array in place using the Enumerable sort algorithm (Quicksort)
*
* @static
* @memberof Enumerable
*/
static sort: (arr: any[], comparer?: IComparer) => any[];
/**
* You should never use this. Instead use Enumerable.from
* @param {IterableType} src
* @memberof Enumerable
*/
constructor(src: IterableType);
/**
* Wraps an iterable item into an Enumerable if it's not already one
*
* @static
* @param {IterableType} iterable
* @returns {Enumerable}
* @memberof Enumerable
*/
static from(iterable: IterableType): Enumerable;
/**
* the Enumerable instance exposes the same iterator as the wrapped iterable or generator function
*
* @returns {Iterator<any>}
* @memberof Enumerable
*/
[Symbol.iterator](): Iterator<any>;
/**
* returns an empty Enumerable
*
* @static
* @returns {Enumerable}
* @memberof Enumerable
*/
static empty(): Enumerable;
/**
* generates a sequence of integer numbers within a specified range.
*
* @static
* @param {number} start
* @param {number} count
* @returns {Enumerable}
* @memberof Enumerable
*/
static range(start: number, count: number): Enumerable;
/**
* Generates a sequence that contains one repeated value.
*
* @static
* @param {*} item
* @param {number} count
* @returns {Enumerable}
* @memberof Enumerable
*/
static repeat(item: any, count: number): Enumerable;
/**
* Same value as count(), but will throw an Error if enumerable is not seekable and has to be iterated to get the length
*/
get length(): number;
/**
* Concatenates two sequences by appending iterable to the existing one.
*
* @param {IterableType} iterable
* @returns {Enumerable}
* @memberof Enumerable
*/
concat(iterable: IterableType): Enumerable;
/**
* Returns distinct elements from a sequence.
* WARNING: using a comparer makes this slower. Not specifying it uses a Set to determine distinctiveness.
*
* @param {IEqualityComparer} [equalityComparer=EqualityComparer.default]
* @returns {Enumerable}
* @memberof Enumerable
*/
distinct(equalityComparer?: IEqualityComparer): Enumerable;
/**
* Returns the element at a specified index in a sequence.
*
* @param {number} index
* @returns {*}
* @memberof Enumerable
*/
elementAt(index: number): any;
/**
* Returns the element at a specified index in a sequence or undefined if the index is out of range.
*
* @param {number} index
* @returns {(any | undefined)}
* @memberof Enumerable
*/
elementAtOrDefault(index: number): any | undefined;
/**
* Returns the first element of a sequence.
*
* @returns {*}
* @memberof Enumerable
*/
first(): any;
/**
* Returns the first element of a sequence, or a default value if no element is found.
*
* @returns {(any | undefined)}
* @memberof Enumerable
*/
firstOrDefault(): any | undefined;
/**
* Returns the last element of a sequence.
*
* @returns {*}
* @memberof Enumerable
*/
last(): any;
/**
* Returns the last element of a sequence, or undefined if no element is found.
*
* @returns {(any | undefined)}
* @memberof Enumerable
*/
lastOrDefault(): any | undefined;
/**
* Returns the count, minimum and maximum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {{ count: number, min: any, max: any }}
* @memberof Enumerable
*/
stats(comparer?: IComparer): {
count: number;
min: any;
max: any;
};
/**
* Returns the minimum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {*}
* @memberof Enumerable
*/
min(comparer?: IComparer): any;
/**
* Returns the maximum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {*}
* @memberof Enumerable
*/
max(comparer?: IComparer): any;
/**
* Projects each element of a sequence into a new form.
*
* @param {ISelector} selector
* @returns {Enumerable}
* @memberof Enumerable
*/
select(selector: ISelector): Enumerable;
/**
* Bypasses a specified number of elements in a sequence and then returns the remaining elements.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
skip(nr: number): Enumerable;
/**
* Takes start elements, ignores howmany elements, continues with the new items and continues with the original enumerable
* Equivalent to the value of an array after performing splice on it with the same parameters
* @param start
* @param howmany
* @param items
* @returns splice
*/
splice(start: number, howmany: number, ...newItems: any[]): Enumerable;
/**
* Computes the sum of a sequence of numeric values.
*
* @returns {(number | undefined)}
* @memberof Enumerable
*/
sum(): number | undefined;
/**
* Computes the sum and count of a sequence of numeric values.
*
* @returns {{ sum: number, count: number }}
* @memberof Enumerable
*/
sumAndCount(): {
sum: number;
count: number;
};
/**
* Returns a specified number of contiguous elements from the start of a sequence.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
take(nr: number): Enumerable;
/**
* creates an array from an Enumerable
*
* @returns {any[]}
* @memberof Enumerable
*/
toArray(): any[];
/**
* similar to toArray, but returns a seekable Enumerable (itself if already seekable) that can do count and elementAt without iterating
*
* @returns {Enumerable}
* @memberof Enumerable
*/
toList(): Enumerable;
/**
* Filters a sequence of values based on a predicate.
*
* @param {IFilter} condition
* @returns {Enumerable}
* @memberof Enumerable
*/
where(condition: IFilter): Enumerable;
}
export function _ensureIterable(src: IterableType): void;
export function _ensureFunction(f: Function): void;
export function _toArray(iterable: IterableType): any[];
export function _ensureInternalCount(enumerable: Enumerable): void;
export function _ensureInternalTryGetAt(enumerable: Enumerable): void;
/**
* an extended iterable type that also supports generator functions
*/
export type IterableType = Iterable<any> | (() => Iterator<any>) | Enumerable;
/**
* A comparer function to be used in sorting
*/
export type IComparer = (item1: any, item2: any) => -1 | 0 | 1;
/**
* A selector function to be used in mapping
*/
export type ISelector<T = any> = (item: any, index?: number) => T;
/**
* A filter function
*/
export type IFilter = ISelector<boolean>;
/**
* The default comparer function between two items
* @param item1
* @param item2
*/
export const _defaultComparer: IComparer;
/**
* Interface for an equality comparer
*/
export type IEqualityComparer = (item1: any, item2: any) => boolean;
/**
* Predefined equality comparers
* default is the equivalent of ==
* exact is the equivalent of ===
*/
export const EqualityComparer: {
default: (item1: any, item2: any) => boolean;
exact: (item1: any, item2: any) => boolean;
};
interface IUsesQuickSort {
_useQuickSort: boolean;
}
export {};
}
declare namespace Linqer {
interface Enumerable extends Iterable<any> {
/**
* Applies an accumulator function over a sequence.
* The specified seed value is used as the initial accumulator value, and the specified function is used to select the result value.
*
* @param {*} accumulator
* @param {(acc: any, item: any) => any} aggregator
* @returns {*}
* @memberof Enumerable
*/
aggregate(accumulator: any, aggregator: (acc: any, item: any) => any): any;
/**
* Determines whether all elements of a sequence satisfy a condition.
* @param condition
* @returns true if all
*/
all(condition: IFilter): boolean;
/**
* Determines whether any element of a sequence exists or satisfies a condition.
*
* @param {IFilter} condition
* @returns {boolean}
* @memberof Enumerable
*/
any(condition: IFilter): boolean;
/**
* Appends a value to the end of the sequence.
*
* @param {*} item
* @returns {Enumerable}
* @memberof Enumerable
*/
append(item: any): Enumerable;
/**
* Computes the average of a sequence of numeric values.
*
* @returns {(number | undefined)}
* @memberof Enumerable
*/
average(): number | undefined;
/**
* Returns itself
*
* @returns {Enumerable}
* @memberof Enumerable
*/
asEnumerable(): Enumerable;
/**
* Checks the elements of a sequence based on their type
* If type is a string, it will check based on typeof, else it will use instanceof.
* Throws if types are different.
* @param {(string | Function)} type
* @returns {Enumerable}
* @memberof Enumerable
*/
cast(type: string | Function): Enumerable;
/**
* Determines whether a sequence contains a specified element.
* A custom function can be used to determine equality between elements.
*
* @param {*} item
* @param {IEqualityComparer} equalityComparer
* @returns {boolean}
* @memberof Enumerable
*/
contains(item: any, equalityComparer: IEqualityComparer): boolean;
defaultIfEmpty(): never;
/**
* Produces the set difference of two sequences
* WARNING: using the comparer is slower
*
* @param {IterableType} iterable
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
except(iterable: IterableType, equalityComparer: IEqualityComparer): Enumerable;
/**
* Produces the set intersection of two sequences.
* WARNING: using a comparer is slower
*
* @param {IterableType} iterable
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
intersect(iterable: IterableType, equalityComparer: IEqualityComparer): Enumerable;
/**
* Same as count
*
* @returns {number}
* @memberof Enumerable
*/
longCount(): number;
/**
* Filters the elements of a sequence based on their type
* If type is a string, it will filter based on typeof, else it will use instanceof
*
* @param {(string | Function)} type
* @returns {Enumerable}
* @memberof Enumerable
*/
ofType(type: string | Function): Enumerable;
/**
* Adds a value to the beginning of the sequence.
*
* @param {*} item
* @returns {Enumerable}
* @memberof Enumerable
*/
prepend(item: any): Enumerable;
/**
* Inverts the order of the elements in a sequence.
*
* @returns {Enumerable}
* @memberof Enumerable
*/
reverse(): Enumerable;
/**
* Projects each element of a sequence to an iterable and flattens the resulting sequences into one sequence.
*
* @param {ISelector<IterableType>} selector
* @returns {Enumerable}
* @memberof Enumerable
*/
selectMany(selector: ISelector<IterableType>): Enumerable;
/**
* Determines whether two sequences are equal and in the same order according to an optional equality comparer.
*
* @param {IterableType} iterable
* @param {IEqualityComparer} equalityComparer
* @returns {boolean}
* @memberof Enumerable
*/
sequenceEqual(iterable: IterableType, equalityComparer: IEqualityComparer): boolean;
/**
* Returns the single element of a sequence and throws if it doesn't have exactly one
*
* @returns {*}
* @memberof Enumerable
*/
single(): any;
/**
* Returns the single element of a sequence or undefined if none found. It throws if the sequence contains multiple items.
*
* @returns {(any | undefined)}
* @memberof Enumerable
*/
singleOrDefault(): any | undefined;
/**
* Returns a new enumerable collection that contains the elements from source with the last nr elements of the source collection omitted.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
skipLast(nr: number): Enumerable;
/**
* Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
*
* @param {IFilter} condition
* @returns {Enumerable}
* @memberof Enumerable
*/
skipWhile(condition: IFilter): Enumerable;
/**
* Selects the elements starting at the given start argument, and ends at, but does not include, the given end argument.
* @param start
* @param end
* @returns slice
*/
slice(start: number | undefined, end: number | undefined): Enumerable;
/**
* Returns a new enumerable collection that contains the last nr elements from source.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
takeLast(nr: number): Enumerable;
/**
* Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements.
*
* @param {IFilter} condition
* @returns {Enumerable}
* @memberof Enumerable
*/
takeWhile(condition: IFilter): Enumerable;
toDictionary(): never;
/**
* creates a Map from an Enumerable
*
* @param {ISelector} keySelector
* @param {ISelector} valueSelector
* @returns {Map<any, any>}
* @memberof Enumerable
*/
toMap(keySelector: ISelector, valueSelector: ISelector): Map<any, any>;
/**
* creates an object from an Enumerable
*
* @param {ISelector} keySelector
* @param {ISelector} valueSelector
* @returns {{ [key: string]: any }}
* @memberof Enumerable
*/
toObject(keySelector: ISelector, valueSelector: ISelector): {
[key: string]: any;
};
toHashSet(): never;
/**
* creates a Set from an enumerable
*
* @returns {Set<any>}
* @memberof Enumerable
*/
toSet(): Set<any>;
/**
* Produces the set union of two sequences.
*
* @param {IterableType} iterable
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
union(iterable: IterableType, equalityComparer: IEqualityComparer): Enumerable;
/**
* Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
*
* @param {IterableType} iterable
* @param {(item1: any, item2: any, index: number) => any} zipper
* @returns {*}
* @memberof Enumerable
*/
zip(iterable: IterableType, zipper: (item1: any, item2: any, index: number) => any): any;
}
}
declare namespace Linqer {
interface Enumerable extends Iterable<any> {
/**
* Groups the elements of a sequence.
*
* @param {ISelector} keySelector
* @returns {Enumerable}
* @memberof Enumerable
*/
groupBy(keySelector: ISelector): Enumerable;
/**
* Correlates the elements of two sequences based on key equality and groups the results. A specified equalityComparer is used to compare keys.
* WARNING: using the equality comparer will be slower
*
* @param {IterableType} iterable
* @param {ISelector} innerKeySelector
* @param {ISelector} outerKeySelector
* @param {(item1: any, item2: any) => any} resultSelector
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
groupJoin(iterable: IterableType, innerKeySelector: ISelector, outerKeySelector: ISelector, resultSelector: (item1: any, item2: any) => any, equalityComparer: IEqualityComparer): Enumerable;
/**
* Correlates the elements of two sequences based on matching keys.
* WARNING: using the equality comparer will be slower
*
* @param {IterableType} iterable
* @param {ISelector} innerKeySelector
* @param {ISelector} outerKeySelector
* @param {(item1: any, item2: any) => any} resultSelector
* @param {IEqualityComparer} equalityComparer
* @returns {Enumerable}
* @memberof Enumerable
*/
join(iterable: IterableType, innerKeySelector: ISelector, outerKeySelector: ISelector, resultSelector: (item1: any, item2: any) => any, equalityComparer: IEqualityComparer): Enumerable;
toLookup(): never;
}
/**
* An Enumerable that also exposes a group key
*
* @export
* @class GroupEnumerable
* @extends {Enumerable}
*/
class GroupEnumerable extends Enumerable {
key: string;
constructor(iterable: IterableType, key: string);
}
}
declare namespace Linqer {
export interface Enumerable extends Iterable<any> {
/**
* Sorts the elements of a sequence in ascending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof Enumerable
*/
orderBy(keySelector: ISelector): OrderedEnumerable;
/**
* Sorts the elements of a sequence in descending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof Enumerable
*/
orderByDescending(keySelector: ISelector): OrderedEnumerable;
/**
* use QuickSort for ordering (default). Recommended when take, skip, takeLast, skipLast are used after orderBy
*
* @returns {Enumerable}
* @memberof Enumerable
*/
useQuickSort(): Enumerable;
/**
* use the default browser sort implementation for ordering at all times
*
* @returns {Enumerable}
* @memberof Enumerable
*/
useBrowserSort(): Enumerable;
}
enum RestrictionType {
skip = 0,
skipLast = 1,
take = 2,
takeLast = 3
}
/**
* An Enumerable yielding ordered items
*
* @export
* @class OrderedEnumerable
* @extends {Enumerable}
*/
export class OrderedEnumerable extends Enumerable {
_keySelectors: {
keySelector: ISelector;
ascending: boolean;
}[];
_restrictions: {
type: RestrictionType;
nr: number;
}[];
/**
*Creates an instance of OrderedEnumerable.
* @param {IterableType} src
* @param {ISelector} [keySelector]
* @param {boolean} [ascending=true]
* @memberof OrderedEnumerable
*/
constructor(src: IterableType, keySelector?: ISelector, ascending?: boolean);
private getSortedArray;
private generateSortFunc;
private getStartAndEndIndexes;
/**
* Performs a subsequent ordering of the elements in a sequence in ascending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
thenBy(keySelector: ISelector): OrderedEnumerable;
/**
* Performs a subsequent ordering of the elements in a sequence in descending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
thenByDescending(keySelector: ISelector): OrderedEnumerable;
/**
* Deferred and optimized implementation of take
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
take(nr: number): OrderedEnumerable;
/**
* Deferred and optimized implementation of takeLast
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
takeLast(nr: number): OrderedEnumerable;
/**
* Deferred and optimized implementation of skip
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
skip(nr: number): OrderedEnumerable;
/**
* Deferred and optimized implementation of skipLast
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
skipLast(nr: number): OrderedEnumerable;
/**
* An optimized implementation of toArray
*
* @returns {any[]}
* @memberof OrderedEnumerable
*/
toArray(): any[];
/**
* An optimized implementation of toMap
*
* @param {ISelector} keySelector
* @param {ISelector} [valueSelector=x => x]
* @returns {Map<any, any>}
* @memberof OrderedEnumerable
*/
toMap(keySelector: ISelector, valueSelector?: ISelector): Map<any, any>;
/**
* An optimized implementation of toObject
*
* @param {ISelector} keySelector
* @param {ISelector} [valueSelector=x => x]
* @returns {{ [key: string]: any }}
* @memberof OrderedEnumerable
*/
toObject(keySelector: ISelector, valueSelector?: ISelector): {
[key: string]: any;
};
/**
* An optimized implementation of to Set
*
* @returns {Set<any>}
* @memberof OrderedEnumerable
*/
toSet(): Set<any>;
}
export {};
}
declare namespace Linqer {
interface Enumerable extends Iterable<any> {
/**
* Returns a randomized sequence of items from an initial source
* @returns shuffle
*/
shuffle(): Enumerable;
/**
* implements random reservoir sampling of k items, with the option to specify a maximum limit for the items
* @param k
* @param limit
* @returns sample
*/
randomSample(k: number, limit: number): Enumerable;
/**
* Returns the count of the items in a sequence. Depending on the sequence type this iterates through it or not.
* @returns count
*/
count(): number;
/**
* returns the distinct values based on a hashing function
*
* @param {ISelector} hashFunc
* @returns {Enumerable}
* @memberof Enumerable
*/
distinctByHash(hashFunc: ISelector): Enumerable;
/**
* returns the values that have different hashes from the items of the iterable provided
*
* @param {IterableType} iterable
* @param {ISelector} hashFunc
* @returns {Enumerable}
* @memberof Enumerable
*/
exceptByHash(iterable: IterableType, hashFunc: ISelector): Enumerable;
/**
* returns the values that have the same hashes as items of the iterable provided
*
* @param {IterableType} iterable
* @param {ISelector} hashFunc
* @returns {Enumerable}
* @memberof Enumerable
*/
intersectByHash(iterable: IterableType, hashFunc: ISelector): Enumerable;
/**
* returns the index of a value in an ordered enumerable or false if not found
* WARNING: use the same comparer as the one used to order the enumerable. The algorithm assumes the enumerable is already sorted.
*
* @param {*} value
* @param {IComparer} comparer
* @returns {(number | boolean)}
* @memberof Enumerable
*/
binarySearch(value: any, comparer: IComparer): number | boolean;
/**
* joins each item of the enumerable with previous items from the same enumerable
* @param offset
* @param zipper
* @returns lag
*/
lag(offset: number, zipper: (item1: any, item2: any) => any): Enumerable;
/**
* joins each item of the enumerable with next items from the same enumerable
*
* @param {number} offset
* @param {(item1: any, item2: any) => any} zipper
* @returns {Enumerable}
* @memberof Enumerable
*/
lead(offset: number, zipper: (item1: any, item2: any) => any): Enumerable;
/**
* returns an enumerable of at least minLength, padding the end with a value or the result of a function
*
* @param {number} minLength
* @param {(any | ((index: number) => any))} filler
* @returns {Enumerable}
* @memberof Enumerable
*/
padEnd(minLength: number, filler: any | ((index: number) => any)): Enumerable;
/**
* returns an enumerable of at least minLength, padding the start with a value or the result of a function
* if the enumerable cannot seek, then it will be iterated minLength time
*
* @param {number} minLength
* @param {(any | ((index: number) => any))} filler
* @returns {Enumerable}
* @memberof Enumerable
*/
padStart(minLength: number, filler: any | ((index: number) => any)): Enumerable;
}
}
export = Linqer;
================================================
FILE: LInQer.all.js
================================================
"use strict";
var Linqer;
(function (Linqer) {
/**
* wrapper class over iterable instances that exposes the methods usually found in .NET LINQ
*
* @export
* @class Enumerable
* @implements {Iterable<any>}
* @implements {IUsesQuickSort}
*/
class Enumerable {
/**
* You should never use this. Instead use Enumerable.from
* @param {IterableType} src
* @memberof Enumerable
*/
constructor(src) {
_ensureIterable(src);
this._src = src;
const iteratorFunction = src[Symbol.iterator];
// the generator is either the iterator of the source enumerable
// or the generator function that was provided as the source itself
if (iteratorFunction) {
this._generator = iteratorFunction.bind(src);
}
else {
this._generator = src;
}
// set sorting method on an enumerable and all the derived ones should inherit it
// TODO: a better method of doing this
this._useQuickSort = src._useQuickSort !== undefined
? src._useQuickSort
: true;
this._canSeek = false;
this._count = null;
this._tryGetAt = null;
this._wasIterated = false;
}
/**
* Wraps an iterable item into an Enumerable if it's not already one
*
* @static
* @param {IterableType} iterable
* @returns {Enumerable}
* @memberof Enumerable
*/
static from(iterable) {
if (iterable instanceof Enumerable)
return iterable;
return new Enumerable(iterable);
}
/**
* the Enumerable instance exposes the same iterator as the wrapped iterable or generator function
*
* @returns {Iterator<any>}
* @memberof Enumerable
*/
[Symbol.iterator]() {
this._wasIterated = true;
return this._generator();
}
/**
* returns an empty Enumerable
*
* @static
* @returns {Enumerable}
* @memberof Enumerable
*/
static empty() {
const result = new Enumerable([]);
result._count = () => 0;
result._tryGetAt = (index) => null;
result._canSeek = true;
return result;
}
/**
* generates a sequence of integer numbers within a specified range.
*
* @static
* @param {number} start
* @param {number} count
* @returns {Enumerable}
* @memberof Enumerable
*/
static range(start, count) {
const gen = function* () {
for (let i = 0; i < count; i++) {
yield start + i;
}
};
const result = new Enumerable(gen);
result._count = () => count;
result._tryGetAt = index => {
if (index >= 0 && index < count)
return { value: start + index };
return null;
};
result._canSeek = true;
return result;
}
/**
* Generates a sequence that contains one repeated value.
*
* @static
* @param {*} item
* @param {number} count
* @returns {Enumerable}
* @memberof Enumerable
*/
static repeat(item, count) {
const gen = function* () {
for (let i = 0; i < count; i++) {
yield item;
}
};
const result = new Enumerable(gen);
result._count = () => count;
result._tryGetAt = index => {
if (index >= 0 && index < count)
return { value: item };
return null;
};
result._canSeek = true;
return result;
}
/**
* Same value as count(), but will throw an Error if enumerable is not seekable and has to be iterated to get the length
*/
get length() {
_ensureInternalTryGetAt(this);
if (!this._canSeek)
throw new Error('Calling length on this enumerable will iterate it. Use count()');
return this.count();
}
/**
* Concatenates two sequences by appending iterable to the existing one.
*
* @param {IterableType} iterable
* @returns {Enumerable}
* @memberof Enumerable
*/
concat(iterable) {
_ensureIterable(iterable);
const self = this;
// the generator will iterate the enumerable first, then the iterable that was given as a parameter
// this will be able to seek if both the original and the iterable derived enumerable can seek
// the indexing function will get items from the first and then second enumerable without iteration
const gen = function* () {
for (const item of self) {
yield item;
}
for (const item of Enumerable.from(iterable)) {
yield item;
}
};
const result = new Enumerable(gen);
const other = Enumerable.from(iterable);
result._count = () => self.count() + other.count();
_ensureInternalTryGetAt(this);
_ensureInternalTryGetAt(other);
result._canSeek = self._canSeek && other._canSeek;
if (self._canSeek) {
result._tryGetAt = index => {
return self._tryGetAt(index) || other._tryGetAt(index - self.count());
};
}
return result;
}
/**
* Returns the number of elements in a sequence.
*
* @returns {number}
* @memberof Enumerable
*/
count() {
_ensureInternalCount(this);
return this._count();
}
/**
* Returns distinct elements from a sequence.
* WARNING: using a comparer makes this slower. Not specifying it uses a Set to determine distinctiveness.
*
* @param {IEqualityComparer} [equalityComparer=EqualityComparer.default]
* @returns {Enumerable}
* @memberof Enumerable
*/
distinct(equalityComparer = Linqer.EqualityComparer.default) {
const self = this;
// if the comparer function is not provided, a Set will be used to quickly determine distinctiveness
const gen = equalityComparer === Linqer.EqualityComparer.default
? function* () {
const distinctValues = new Set();
for (const item of self) {
const size = distinctValues.size;
distinctValues.add(item);
if (size < distinctValues.size) {
yield item;
}
}
}
// otherwise values will be compared with previous values ( O(n^2) )
// use distinctByHash in Linqer.extra to use a hashing function ( O(n log n) )
: function* () {
const values = [];
for (const item of self) {
let unique = true;
for (let i = 0; i < values.length; i++) {
if (equalityComparer(item, values[i])) {
unique = false;
break;
}
}
if (unique)
yield item;
values.push(item);
}
};
return new Enumerable(gen);
}
/**
* Returns the element at a specified index in a sequence.
*
* @param {number} index
* @returns {*}
* @memberof Enumerable
*/
elementAt(index) {
_ensureInternalTryGetAt(this);
const result = this._tryGetAt(index);
if (!result)
throw new Error('Index out of range');
return result.value;
}
/**
* Returns the element at a specified index in a sequence or undefined if the index is out of range.
*
* @param {number} index
* @returns {(any | undefined)}
* @memberof Enumerable
*/
elementAtOrDefault(index) {
_ensureInternalTryGetAt(this);
const result = this._tryGetAt(index);
if (!result)
return undefined;
return result.value;
}
/**
* Returns the first element of a sequence.
*
* @returns {*}
* @memberof Enumerable
*/
first() {
return this.elementAt(0);
}
/**
* Returns the first element of a sequence, or a default value if no element is found.
*
* @returns {(any | undefined)}
* @memberof Enumerable
*/
firstOrDefault() {
return this.elementAtOrDefault(0);
}
/**
* Returns the last element of a sequence.
*
* @returns {*}
* @memberof Enumerable
*/
last() {
_ensureInternalTryGetAt(this);
// if this cannot seek, getting the last element requires iterating the whole thing
if (!this._canSeek) {
let result = null;
let found = false;
for (const item of this) {
result = item;
found = true;
}
if (found)
return result;
throw new Error('The enumeration is empty');
}
// if this can seek, then just go directly at the last element
const count = this.count();
return this.elementAt(count - 1);
}
/**
* Returns the last element of a sequence, or undefined if no element is found.
*
* @returns {(any | undefined)}
* @memberof Enumerable
*/
lastOrDefault() {
_ensureInternalTryGetAt(this);
if (!this._canSeek) {
let result = undefined;
for (const item of this) {
result = item;
}
return result;
}
const count = this.count();
return this.elementAtOrDefault(count - 1);
}
/**
* Returns the count, minimum and maximum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {{ count: number, min: any, max: any }}
* @memberof Enumerable
*/
stats(comparer) {
if (comparer) {
_ensureFunction(comparer);
}
else {
comparer = Linqer._defaultComparer;
}
const agg = {
count: 0,
min: undefined,
max: undefined
};
for (const item of this) {
if (typeof agg.min === 'undefined' || comparer(item, agg.min) < 0)
agg.min = item;
if (typeof agg.max === 'undefined' || comparer(item, agg.max) > 0)
agg.max = item;
agg.count++;
}
return agg;
}
/**
* Returns the minimum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {*}
* @memberof Enumerable
*/
min(comparer) {
const stats = this.stats(comparer);
return stats.count === 0
? undefined
: stats.min;
}
/**
* Returns the maximum value in a sequence of values.
* A custom function can be used to establish order (the result 0 means equal, 1 means larger, -1 means smaller)
*
* @param {IComparer} [comparer]
* @returns {*}
* @memberof Enumerable
*/
max(comparer) {
const stats = this.stats(comparer);
return stats.count === 0
? undefined
: stats.max;
}
/**
* Projects each element of a sequence into a new form.
*
* @param {ISelector} selector
* @returns {Enumerable}
* @memberof Enumerable
*/
select(selector) {
_ensureFunction(selector);
const self = this;
// the generator is applying the selector on all the items of the enumerable
// the count of the resulting enumerable is the same as the original's
// the indexer is the same as that of the original, with the selector applied on the value
const gen = function* () {
let index = 0;
for (const item of self) {
yield selector(item, index);
index++;
}
};
const result = new Enumerable(gen);
_ensureInternalCount(this);
result._count = this._count;
_ensureInternalTryGetAt(self);
result._canSeek = self._canSeek;
result._tryGetAt = index => {
const res = self._tryGetAt(index);
if (!res)
return res;
return { value: selector(res.value) };
};
return result;
}
/**
* Bypasses a specified number of elements in a sequence and then returns the remaining elements.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
skip(nr) {
const self = this;
// the generator just enumerates the first nr numbers then starts yielding values
// the count is the same as the original enumerable, minus the skipped items and at least 0
// the indexer is the same as for the original, with an offset
const gen = function* () {
let nrLeft = nr;
for (const item of self) {
if (nrLeft > 0) {
nrLeft--;
}
else {
yield item;
}
}
};
const result = new Enumerable(gen);
result._count = () => Math.max(0, self.count() - nr);
_ensureInternalTryGetAt(this);
result._canSeek = this._canSeek;
result._tryGetAt = index => self._tryGetAt(index + nr);
return result;
}
/**
* Takes start elements, ignores howmany elements, continues with the new items and continues with the original enumerable
* Equivalent to the value of an array after performing splice on it with the same parameters
* @param start
* @param howmany
* @param items
* @returns splice
*/
splice(start, howmany, ...newItems) {
// tried to define length and splice so that this is seen as an Array-like object,
// but it doesn't work on properties. length needs to be a field.
return this.take(start).concat(newItems).concat(this.skip(start + howmany));
}
/**
* Computes the sum of a sequence of numeric values.
*
* @returns {(number | undefined)}
* @memberof Enumerable
*/
sum() {
const stats = this.sumAndCount();
return stats.count === 0
? undefined
: stats.sum;
}
/**
* Computes the sum and count of a sequence of numeric values.
*
* @returns {{ sum: number, count: number }}
* @memberof Enumerable
*/
sumAndCount() {
const agg = {
count: 0,
sum: 0
};
for (const item of this) {
agg.sum = agg.count === 0
? _toNumber(item)
: agg.sum + _toNumber(item);
agg.count++;
}
return agg;
}
/**
* Returns a specified number of contiguous elements from the start of a sequence.
*
* @param {number} nr
* @returns {Enumerable}
* @memberof Enumerable
*/
take(nr) {
const self = this;
// the generator will stop after nr items yielded
// the count is the maximum between the total count and nr
// the indexer is the same, as long as it's not higher than nr
const gen = function* () {
let nrLeft = nr;
for (const item of self) {
if (nrLeft > 0) {
yield item;
nrLeft--;
}
if (nrLeft <= 0) {
break;
}
}
};
const result = new Enumerable(gen);
result._count = () => Math.min(nr, self.count());
_ensureInternalTryGetAt(this);
result._canSeek = self._canSeek;
if (self._canSeek) {
result._tryGetAt = index => {
if (index >= nr)
return null;
return self._tryGetAt(index);
};
}
return result;
}
/**
* creates an array from an Enumerable
*
* @returns {any[]}
* @memberof Enumerable
*/
toArray() {
var _a;
_ensureInternalTryGetAt(this);
// this should be faster than Array.from(this)
if (this._canSeek) {
const arr = new Array(this.count());
for (let i = 0; i < arr.length; i++) {
arr[i] = (_a = this._tryGetAt(i)) === null || _a === void 0 ? void 0 : _a.value;
}
return arr;
}
// try to optimize the array growth by increasing it
// by 64 every time it is needed
const minIncrease = 64;
let size = 0;
const arr = [];
for (const item of this) {
if (size === arr.length) {
arr.length += minIncrease;
}
arr[size] = item;
size++;
}
arr.length = size;
return arr;
}
/**
* similar to toArray, but returns a seekable Enumerable (itself if already seekable) that can do count and elementAt without iterating
*
* @returns {Enumerable}
* @memberof Enumerable
*/
toList() {
_ensureInternalTryGetAt(this);
if (this._canSeek)
return this;
return Enumerable.from(this.toArray());
}
/**
* Filters a sequence of values based on a predicate.
*
* @param {IFilter} condition
* @returns {Enumerable}
* @memberof Enumerable
*/
where(condition) {
_ensureFunction(condition);
const self = this;
// cannot imply the count or indexer from the condition
// where will have to iterate through the whole thing
const gen = function* () {
let index = 0;
for (const item of self) {
if (condition(item, index)) {
yield item;
}
index++;
}
};
return new Enumerable(gen);
}
}
Linqer.Enumerable = Enumerable;
// throw if src is not a generator function or an iteratable
function _ensureIterable(src) {
if (src) {
if (src[Symbol.iterator])
return;
if (typeof src === 'function' && src.constructor.name === 'GeneratorFunction')
return;
}
throw new Error('the argument must be iterable!');
}
Linqer._ensureIterable = _ensureIterable;
// throw if f is not a function
function _ensureFunction(f) {
if (!f || typeof f !== 'function')
throw new Error('the argument needs to be a function!');
}
Linqer._ensureFunction = _ensureFunction;
// return Nan if this is not a number
// different from Number(obj), which would cast strings to numbers
function _toNumber(obj) {
return typeof obj === 'number'
? obj
: Number.NaN;
}
// return the iterable if already an array or use Array.from to create one
function _toArray(iterable) {
if (!iterable)
return [];
if (Array.isArray(iterable))
return iterable;
return Array.from(iterable);
}
Linqer._toArray = _toArray;
// if the internal count function is not defined, set it to the most appropriate one
function _ensureInternalCount(enumerable) {
if (enumerable._count)
return;
if (enumerable._src instanceof Enumerable) {
// the count is the same as the underlying enumerable
const innerEnumerable = enumerable._src;
_ensureInternalCount(innerEnumerable);
enumerable._count = () => innerEnumerable._count();
return;
}
const src = enumerable._src;
// this could cause false positives, but if it has a numeric length or size, use it
if (typeof src !== 'function' && typeof src.length === 'number') {
enumerable._count = () => src.length;
return;
}
if (typeof src.size === 'number') {
enumerable._count = () => src.size;
return;
}
// otherwise iterate the whole thing and count all items
enumerable._count = () => {
let x = 0;
for (const item of enumerable)
x++;
return x;
};
}
Linqer._ensureInternalCount = _ensureInternalCount;
// ensure there is an internal indexer function adequate for this enumerable
// this also determines if the enumerable can seek
function _ensureInternalTryGetAt(enumerable) {
if (enumerable._tryGetAt)
return;
enumerable._canSeek = true;
if (enumerable._src instanceof Enumerable) {
// indexer and seekability is the same as for the underlying enumerable
const innerEnumerable = enumerable._src;
_ensureInternalTryGetAt(innerEnumerable);
enumerable._tryGetAt = index => innerEnumerable._tryGetAt(index);
enumerable._canSeek = innerEnumerable._canSeek;
return;
}
if (typeof enumerable._src === 'string') {
// a string can be accessed by index
enumerable._tryGetAt = index => {
if (index < enumerable._src.length) {
return { value: enumerable._src.charAt(index) };
}
return null;
};
return;
}
if (Array.isArray(enumerable._src)) {
// an array can be accessed by index
enumerable._tryGetAt = index => {
if (index >= 0 && index < enumerable._src.length) {
return { value: enumerable._src[index] };
}
return null;
};
return;
}
const src = enumerable._src;
if (typeof enumerable._src !== 'function' && typeof src.length === 'number') {
// try to access an object with a defined numeric length by indexing it
// might cause false positives
enumerable._tryGetAt = index => {
if (index < src.length && typeof src[index] !== 'undefined') {
return { value: src[index] };
}
return null;
};
return;
}
enumerable._canSeek = false;
// TODO other specialized types? objects, maps, sets?
enumerable._tryGetAt = index => {
let x = 0;
for (const item of enumerable) {
if (index === x)
return { value: item };
x++;
}
return null;
};
}
Linqer._ensureInternalTryGetAt = _ensureInternalTryGetAt;
/**
* The default comparer function between two items
* @param item1
* @param item2
*/
Linqer._defaultComparer = (item1, item2) => {
if (item1 > item2)
return 1;
if (item1 < item2)
return -1;
return 0;
};
/**
* Predefined equality comparers
* default is the equivalent of ==
* exact is the equivalent of ===
*/
Linqer.EqualityComparer = {
default: (item1, item2) => item1 == item2,
exact: (item1, item2) => item1 === item2,
};
})(Linqer || (Linqer = {}));
/// <reference path="./LInQer.Slim.ts" />
var Linqer;
/// <reference path="./LInQer.Slim.ts" />
(function (Linqer) {
/// Applies an accumulator function over a sequence.
/// The specified seed value is used as the initial accumulator value, and the specified function is used to select the result value.
Linqer.Enumerable.prototype.aggregate = function (accumulator, aggregator) {
Linqer._ensureFunction(aggregator);
for (const item of this) {
accumulator = aggregator(accumulator, item);
}
return accumulator;
};
/// Determines whether all elements of a sequence satisfy a condition.
Linqer.Enumerable.prototype.all = function (condition) {
Linqer._ensureFunction(condition);
return !this.any(x => !condition(x));
};
/// Determines whether any element of a sequence exists or satisfies a condition.
Linqer.Enumerable.prototype.any = function (condition) {
Linqer._ensureFunction(condition);
let index = 0;
for (const item of this) {
if (condition(item, index))
return true;
index++;
}
return false;
};
/// Appends a value to the end of the sequence.
Linqer.Enumerable.prototype.append = function (item) {
return this.concat([item]);
};
/// Computes the average of a sequence of numeric values.
Linqer.Enumerable.prototype.average = function () {
const stats = this.sumAndCount();
return stats.count === 0
? undefined
: stats.sum / stats.count;
};
/// Returns the same enumerable
Linqer.Enumerable.prototype.asEnumerable = function () {
return this;
};
/// Checks the elements of a sequence based on their type
/// If type is a string, it will check based on typeof, else it will use instanceof.
/// Throws if types are different.
Linqer.Enumerable.prototype.cast = function (type) {
const f = typeof type === 'string'
? x => typeof x === type
: x => x instanceof type;
return this.select(item => {
if (!f(item))
throw new Error(item + ' not of type ' + type);
return item;
});
};
/// Determines whether a sequence contains a specified element.
/// A custom function can be used to determine equality between elements.
Linqer.Enumerable.prototype.contains = function (item, equalityComparer = Linqer.EqualityComparer.default) {
Linqer._ensureFunction(equalityComparer);
return this.any(x => equalityComparer(x, item));
};
Linqer.Enumerable.prototype.defaultIfEmpty = function () {
throw new Error('defaultIfEmpty not implemented for Javascript');
};
/// Produces the set difference of two sequences WARNING: using the comparer is slower
Linqer.Enumerable.prototype.except = function (iterable, equalityComparer = Linqer.EqualityComparer.default) {
Linqer._ensureIterable(iterable);
const self = this;
// use a Set for performance if the comparer is not set
const gen = equalityComparer === Linqer.EqualityComparer.default
? function* () {
const distinctValues = Linqer.Enumerable.from(iterable).toSet();
for (const item of self) {
if (!distinctValues.has(item))
yield item;
}
}
// use exceptByHash from Linqer.extra for better performance
: function* () {
const values = Linqer._toArray(iterable);
for (const item of self) {
let unique = true;
for (let i = 0; i < values.length; i++) {
if (equalityComparer(item, values[i])) {
unique = false;
break;
}
}
if (unique)
yield item;
}
};
return new Linqer.Enumerable(gen);
};
/// Produces the set intersection of two sequences. WARNING: using a comparer is slower
Linqer.Enumerable.prototype.intersect = function (iterable, equalityComparer = Linqer.EqualityComparer.default) {
Linqer._ensureIterable(iterable);
const self = this;
// use a Set for performance if the comparer is not set
const gen = equalityComparer === Linqer.EqualityComparer.default
? function* () {
const distinctValues = new Set(Linqer.Enumerable.from(iterable));
for (const item of self) {
if (distinctValues.has(item))
yield item;
}
}
// use intersectByHash from Linqer.extra for better performance
: function* () {
const values = Linqer._toArray(iterable);
for (const item of self) {
let unique = true;
for (let i = 0; i < values.length; i++) {
if (equalityComparer(item, values[i])) {
unique = false;
break;
}
}
if (!unique)
yield item;
}
};
return new Linqer.Enumerable(gen);
};
/// same as count
Linqer.Enumerable.prototype.longCount = function () {
return this.count();
};
/// Filters the elements of a sequence based on their type
/// If type is a string, it will filter based on typeof, else it will use instanceof
Linqer.Enumerable.prototype.ofType = function (type) {
const condition = typeof type === 'string'
? x => typeof x === type
: x => x instanceof type;
return this.where(condition);
};
/// Adds a value to the beginning of the sequence.
Linqer.Enumerable.prototype.prepend = function (item) {
return new Linqer.Enumerable([item]).concat(this);
};
/// Inverts the order of the elements in a sequence.
Linqer.Enumerable.prototype.reverse = function () {
Linqer._ensureInternalTryGetAt(this);
const self = this;
// if it can seek, just read the enumerable backwards
const gen = this._canSeek
? function* () {
const length = self.count();
for (let index = length - 1; index >= 0; index--) {
yield self.elementAt(index);
}
}
// else enumerate it all into an array, then read it backwards
: function* () {
const arr = self.toArray();
for (let index = arr.length - 1; index >= 0; index--) {
yield arr[index];
}
};
// the count is the same when reversed
const result = new Linqer.Enumerable(gen);
Linqer._ensureInternalCount(this);
result._count = this._count;
Linqer._ensureInternalTryGetAt(this);
// have a custom indexer only if the original enumerable could seek
if (this._canSeek) {
const self = this;
result._canSeek = true;
result._tryGetAt = index => self._tryGetAt(self.count() - index - 1);
}
return result;
};
/// Projects each element of a sequence to an iterable and flattens the resulting sequences into one sequence.
Linqer.Enumerable.prototype.selectMany = function (selector) {
if (typeof selector !== 'undefined') {
Linqer._ensureFunction(selector);
}
else {
selector = x => x;
}
const self = this;
const gen = function* () {
let index = 0;
for (const item of self) {
const iter = selector(item, index);
Linqer._ensureIterable(iter);
for (const child of iter) {
yield child;
}
index++;
}
};
return new Linqer.Enumerable(gen);
};
/// Determines whether two sequences are equal and in the same order according to an equality comparer.
Linqer.Enumerable.prototype.sequenceEqual = function (iterable, equalityComparer = Linqer.EqualityComparer.default) {
Linqer._ensureIterable(iterable);
Linqer._ensureFunction(equalityComparer);
const iterator1 = this[Symbol.iterator]();
const iterator2 = Linqer.Enumerable.from(iterable)[Symbol.iterator]();
let done = false;
do {
const val1 = iterator1.next();
const val2 = iterator2.next();
const equal = (val1.done && val2.done) || (!val1.done && !val2.done && equalityComparer(val1.value, val2.value));
if (!equal)
return false;
done = !!val1.done;
} while (!done);
return true;
};
/// Returns the single element of a sequence and throws if it doesn't have exactly one
Linqer.Enumerable.prototype.single = function () {
const iterator = this[Symbol.iterator]();
let val = iterator.next();
if (val.done)
throw new Error('Sequence contains no elements');
const result = val.value;
val = iterator.next();
if (!val.done)
throw new Error('Sequence contains more than one element');
return result;
};
/// Returns the single element of a sequence or undefined if none found. It throws if the sequence contains multiple items.
Linqer.Enumerable.prototype.singleOrDefault = function () {
const iterator = this[Symbol.iterator]();
let val = iterator.next();
if (val.done)
return undefined;
const result = val.value;
val = iterator.next();
if (!val.done)
throw new Error('Sequence contains more than one element');
return result;
};
/// Selects the elements starting at the given start argument, and ends at, but does not include, the given end argument.
Linqer.Enumerable.prototype.slice = function (start = 0, end) {
let enumerable = this;
// when the end is defined and positive and start is negative,
// the only way to compute the last index is to know the count
if (end !== undefined && end >= 0 && (start || 0) < 0) {
enumerable = enumerable.toList();
start = enumerable.count() + start;
}
if (start !== 0) {
if (start > 0) {
enumerable = enumerable.skip(start);
}
else {
enumerable = enumerable.takeLast(-start);
}
}
if (end !== undefined) {
if (end >= 0) {
enumerable = enumerable.take(end - start);
}
else {
enumerable = enumerable.skipLast(-end);
}
}
return enumerable;
};
/// Returns a new enumerable collection that contains the elements from source with the last nr elements of the source collection omitted.
Linqer.Enumerable.prototype.skipLast = function (nr) {
const self = this;
// the generator is using a buffer to cache nr values
// and only yields the values that overflow from it
const gen = function* () {
let nrLeft = nr;
const buffer = Array(nrLeft);
let index = 0;
let offset = 0;
for (const item of self) {
const value = buffer[index - offset];
buffer[index - offset] = item;
index++;
if (index - offset >= nrLeft) {
offset += nrLeft;
}
if (index > nrLeft) {
yield value;
}
}
buffer.length = 0;
};
const result = new Linqer.Enumerable(gen);
// the count is the original count minus the skipped items and at least 0
result._count = () => Math.max(0, self.count() - nr);
Linqer._ensureInternalTryGetAt(this);
result._canSeek = this._canSeek;
// it has an indexer only if the original enumerable can seek
if (this._canSeek) {
result._tryGetAt = index => {
if (index >= result.count())
return null;
return self._tryGetAt(index);
};
}
return result;
};
/// Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
Linqer.Enumerable.prototype.skipWhile = function (condition) {
Linqer._ensureFunction(condition);
const self = this;
let skip = true;
const gen = function* () {
let index = 0;
for (const item of self) {
if (skip && !condition(item, index)) {
skip = false;
}
if (!skip) {
yield item;
}
index++;
}
};
return new Linqer.Enumerable(gen);
};
/// Returns a new enumerable collection that contains the last nr elements from source.
Linqer.Enumerable.prototype.takeLast = function (nr) {
Linqer._ensureInternalTryGetAt(this);
const self = this;
const gen = this._canSeek
// taking the last items is easy if the enumerable can seek
? function* () {
let nrLeft = nr;
const length = self.count();
for (let index = length - nrLeft; index < length; index++) {
yield self.elementAt(index);
}
}
// else the generator uses a buffer to fill with values
// and yields them after the entire thing has been iterated
: function* () {
let nrLeft = nr;
let index = 0;
const buffer = Array(nrLeft);
for (const item of self) {
buffer[index % nrLeft] = item;
index++;
}
for (let i = 0; i < nrLeft && i < index; i++) {
yield buffer[(index + i) % nrLeft];
}
};
const result = new Linqer.Enumerable(gen);
// the count is the minimum between nr and the enumerable count
result._count = () => Math.min(nr, self.count());
result._canSeek = self._canSeek;
// this can seek only if the original enumerable could seek
if (self._canSeek) {
result._tryGetAt = index => {
if (index < 0 || index >= result.count())
return null;
return self._tryGetAt(self.count() - nr + index);
};
}
return result;
};
/// Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements.
Linqer.Enumerable.prototype.takeWhile = function (condition) {
Linqer._ensureFunction(condition);
const self = this;
const gen = function* () {
let index = 0;
for (const item of self) {
if (condition(item, index)) {
yield item;
}
else {
break;
}
index++;
}
};
return new Linqer.Enumerable(gen);
};
Linqer.Enumerable.prototype.toDictionary = function () {
throw new Error('use toMap or toObject instead of toDictionary');
};
/// creates a map from an Enumerable
Linqer.Enumerable.prototype.toMap = function (keySelector, valueSelector = x => x) {
Linqer._ensureFunction(keySelector);
Linqer._ensureFunction(valueSelector);
const result = new Map();
let index = 0;
for (const item of this) {
result.set(keySelector(item, index), valueSelector(item, index));
index++;
}
return result;
};
/// creates an object from an enumerable
Linqer.Enumerable.prototype.toObject = function (keySelector, valueSelector = x => x) {
Linqer._ensureFunction(keySelector);
Linqer._ensureFunction(valueSelector);
const result = {};
let index = 0;
for (const item of this) {
result[keySelector(item, index)] = valueSelector(item);
index++;
}
return result;
};
Linqer.Enumerable.prototype.toHashSet = function () {
throw new Error('use toSet instead of toHashSet');
};
/// creates a set from an enumerable
Linqer.Enumerable.prototype.toSet = function () {
const result = new Set();
for (const item of this) {
result.add(item);
}
return result;
};
/// Produces the set union of two sequences.
Linqer.Enumerable.prototype.union = function (iterable, equalityComparer = Linqer.EqualityComparer.default) {
Linqer._ensureIterable(iterable);
return this.concat(iterable).distinct(equalityComparer);
};
/// Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
Linqer.Enumerable.prototype.zip = function (iterable, zipper) {
Linqer._ensureIterable(iterable);
if (!zipper) {
zipper = (i1, i2) => [i1, i2];
}
else {
Linqer._ensureFunction(zipper);
}
const self = this;
const gen = function* () {
let index = 0;
const iterator1 = self[Symbol.iterator]();
const iterator2 = Linqer.Enumerable.from(iterable)[Symbol.iterator]();
let done = false;
do {
const val1 = iterator1.next();
const val2 = iterator2.next();
done = !!(val1.done || val2.done);
if (!done) {
yield zipper(val1.value, val2.value, index);
}
index++;
} while (!done);
};
return new Linqer.Enumerable(gen);
};
})(Linqer || (Linqer = {}));
/// <reference path="./LInQer.Slim.ts" />
var Linqer;
/// <reference path="./LInQer.Slim.ts" />
(function (Linqer) {
/// Groups the elements of a sequence.
Linqer.Enumerable.prototype.groupBy = function (keySelector) {
Linqer._ensureFunction(keySelector);
const self = this;
const gen = function* () {
const groupMap = new Map();
let index = 0;
// iterate all items and group them in a Map
for (const item of self) {
const key = keySelector(item, index);
const group = groupMap.get(key);
if (group) {
group.push(item);
}
else {
groupMap.set(key, [item]);
}
index++;
}
// then yield a GroupEnumerable for each group
for (const [key, items] of groupMap) {
const group = new GroupEnumerable(items, key);
yield group;
}
};
const result = new Linqer.Enumerable(gen);
return result;
};
/// Correlates the elements of two sequences based on key equality and groups the results. A specified equalityComparer is used to compare keys.
/// WARNING: using the equality comparer will be slower
Linqer.Enumerable.prototype.groupJoin = function (iterable, innerKeySelector, outerKeySelector, resultSelector, equalityComparer = Linqer.EqualityComparer.default) {
const self = this;
const gen = equalityComparer === Linqer.EqualityComparer.default
? function* () {
const lookup = new Linqer.Enumerable(iterable)
.groupBy(outerKeySelector)
.toMap(g => g.key, g => g);
let index = 0;
for (const innerItem of self) {
const arr = Linqer._toArray(lookup.get(innerKeySelector(innerItem, index)));
yield resultSelector(innerItem, arr);
index++;
}
}
: function* () {
let innerIndex = 0;
for (const innerItem of self) {
const arr = [];
let outerIndex = 0;
for (const outerItem of Linqer.Enumerable.from(iterable)) {
if (equalityComparer(innerKeySelector(innerItem, innerIndex), outerKeySelector(outerItem, outerIndex))) {
arr.push(outerItem);
}
outerIndex++;
}
yield resultSelector(innerItem, arr);
innerIndex++;
}
};
return new Linqer.Enumerable(gen);
};
/// Correlates the elements of two sequences based on matching keys.
/// WARNING: using the equality comparer will be slower
Linqer.Enumerable.prototype.join = function (iterable, innerKeySelector, outerKeySelector, resultSelector, equalityComparer = Linqer.EqualityComparer.default) {
const self = this;
const gen = equalityComparer === Linqer.EqualityComparer.default
? function* () {
const lookup = new Linqer.Enumerable(iterable)
.groupBy(outerKeySelector)
.toMap(g => g.key, g => g);
let index = 0;
for (const innerItem of self) {
const group = lookup.get(innerKeySelector(innerItem, index));
if (group) {
for (const outerItem of group) {
yield resultSelector(innerItem, outerItem);
}
}
index++;
}
}
: function* () {
let innerIndex = 0;
for (const innerItem of self) {
let outerIndex = 0;
for (const outerItem of Linqer.Enumerable.from(iterable)) {
if (equalityComparer(innerKeySelector(innerItem, innerIndex), outerKeySelector(outerItem, outerIndex))) {
yield resultSelector(innerItem, outerItem);
}
outerIndex++;
}
innerIndex++;
}
};
return new Linqer.Enumerable(gen);
};
Linqer.Enumerable.prototype.toLookup = function () {
throw new Error('use groupBy instead of toLookup');
};
/**
* An Enumerable that also exposes a group key
*
* @export
* @class GroupEnumerable
* @extends {Enumerable}
*/
class GroupEnumerable extends Linqer.Enumerable {
constructor(iterable, key) {
super(iterable);
this.key = key;
}
}
Linqer.GroupEnumerable = GroupEnumerable;
})(Linqer || (Linqer = {}));
/// <reference path="./LInQer.Slim.ts" />
var Linqer;
/// <reference path="./LInQer.Slim.ts" />
(function (Linqer) {
/// Sorts the elements of a sequence in ascending order.
Linqer.Enumerable.prototype.orderBy = function (keySelector) {
if (keySelector) {
Linqer._ensureFunction(keySelector);
}
else {
keySelector = item => item;
}
return new OrderedEnumerable(this, keySelector, true);
};
/// Sorts the elements of a sequence in descending order.
Linqer.Enumerable.prototype.orderByDescending = function (keySelector) {
if (keySelector) {
Linqer._ensureFunction(keySelector);
}
else {
keySelector = item => item;
}
return new OrderedEnumerable(this, keySelector, false);
};
/// use QuickSort for ordering (default). Recommended when take, skip, takeLast, skipLast are used after orderBy
Linqer.Enumerable.prototype.useQuickSort = function () {
this._useQuickSort = true;
return this;
};
/// use the default browser sort implementation for ordering at all times
Linqer.Enumerable.prototype.useBrowserSort = function () {
this._useQuickSort = false;
return this;
};
//static sort: (arr: any[], comparer?: IComparer) => void;
Linqer.Enumerable.sort = function (arr, comparer = Linqer._defaultComparer) {
_quickSort(arr, 0, arr.length - 1, comparer, 0, Number.MAX_SAFE_INTEGER);
return arr;
};
let RestrictionType;
(function (RestrictionType) {
RestrictionType[RestrictionType["skip"] = 0] = "skip";
RestrictionType[RestrictionType["skipLast"] = 1] = "skipLast";
RestrictionType[RestrictionType["take"] = 2] = "take";
RestrictionType[RestrictionType["takeLast"] = 3] = "takeLast";
})(RestrictionType || (RestrictionType = {}));
/**
* An Enumerable yielding ordered items
*
* @export
* @class OrderedEnumerable
* @extends {Enumerable}
*/
class OrderedEnumerable extends Linqer.Enumerable {
/**
*Creates an instance of OrderedEnumerable.
* @param {IterableType} src
* @param {ISelector} [keySelector]
* @param {boolean} [ascending=true]
* @memberof OrderedEnumerable
*/
constructor(src, keySelector, ascending = true) {
super(src);
this._keySelectors = [];
this._restrictions = [];
if (keySelector) {
this._keySelectors.push({ keySelector: keySelector, ascending: ascending });
}
const self = this;
// generator gets an array of the original,
// sorted inside the interval determined by functions such as skip, take, skipLast, takeLast
this._generator = function* () {
let { startIndex, endIndex, arr } = this.getSortedArray();
if (arr) {
for (let index = startIndex; index < endIndex; index++) {
yield arr[index];
}
}
};
// the count is the difference between the end and start indexes
// if no skip/take functions were used, this will be the original count
this._count = () => {
const totalCount = Linqer.Enumerable.from(self._src).count();
const { startIndex, endIndex } = this.getStartAndEndIndexes(self._restrictions, totalCount);
return endIndex - startIndex;
};
// an ordered enumerable cannot seek
this._canSeek = false;
this._tryGetAt = () => { throw new Error('Ordered enumerables cannot seek'); };
}
getSortedArray() {
const self = this;
let startIndex;
let endIndex;
let arr = null;
const innerEnumerable = self._src;
Linqer._ensureInternalTryGetAt(innerEnumerable);
// try to avoid enumerating the entire original into an array
if (innerEnumerable._canSeek) {
({ startIndex, endIndex } = self.getStartAndEndIndexes(self._restrictions, innerEnumerable.count()));
}
else {
arr = Array.from(self._src);
({ startIndex, endIndex } = self.getStartAndEndIndexes(self._restrictions, arr.length));
}
if (startIndex < endIndex) {
if (!arr) {
arr = Array.from(self._src);
}
// only quicksort supports partial ordering inside an interval
const sort = self._useQuickSort
? (a, c) => _quickSort(a, 0, a.length - 1, c, startIndex, endIndex)
: (a, c) => a.sort(c);
const sortFunc = self.generateSortFunc(self._keySelectors);
sort(arr, sortFunc);
return {
startIndex,
endIndex,
arr
};
}
else {
return {
startIndex,
endIndex,
arr: null
};
}
}
generateSortFunc(selectors) {
// simplify the selectors into an array of comparers
const comparers = selectors.map(s => {
const f = s.keySelector;
const comparer = (i1, i2) => {
const k1 = f(i1);
const k2 = f(i2);
if (k1 > k2)
return 1;
if (k1 < k2)
return -1;
return 0;
};
return s.ascending
? comparer
: (i1, i2) => -comparer(i1, i2);
});
// optimize the resulting sort function in the most common case
// (ordered by a single criterion)
return comparers.length == 1
? comparers[0]
: (i1, i2) => {
for (let i = 0; i < comparers.length; i++) {
const v = comparers[i](i1, i2);
if (v)
return v;
}
return 0;
};
}
/// calculate the interval in which an array needs to have ordered items for this ordered enumerable
getStartAndEndIndexes(restrictions, arrLength) {
let startIndex = 0;
let endIndex = arrLength;
for (const restriction of restrictions) {
switch (restriction.type) {
case RestrictionType.take:
endIndex = Math.min(endIndex, startIndex + restriction.nr);
break;
case RestrictionType.skip:
startIndex = Math.min(endIndex, startIndex + restriction.nr);
break;
case RestrictionType.takeLast:
startIndex = Math.max(startIndex, endIndex - restriction.nr);
break;
case RestrictionType.skipLast:
endIndex = Math.max(startIndex, endIndex - restriction.nr);
break;
}
}
return { startIndex, endIndex };
}
/**
* Performs a subsequent ordering of the elements in a sequence in ascending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
thenBy(keySelector) {
this._keySelectors.push({ keySelector: keySelector, ascending: true });
return this;
}
/**
* Performs a subsequent ordering of the elements in a sequence in descending order.
*
* @param {ISelector} keySelector
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
thenByDescending(keySelector) {
this._keySelectors.push({ keySelector: keySelector, ascending: false });
return this;
}
/**
* Deferred and optimized implementation of take
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
take(nr) {
this._restrictions.push({ type: RestrictionType.take, nr: nr });
return this;
}
/**
* Deferred and optimized implementation of takeLast
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
takeLast(nr) {
this._restrictions.push({ type: RestrictionType.takeLast, nr: nr });
return this;
}
/**
* Deferred and optimized implementation of skip
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
skip(nr) {
this._restrictions.push({ type: RestrictionType.skip, nr: nr });
return this;
}
/**
* Deferred and optimized implementation of skipLast
*
* @param {number} nr
* @returns {OrderedEnumerable}
* @memberof OrderedEnumerable
*/
skipLast(nr) {
this._restrictions.push({ type: RestrictionType.skipLast, nr: nr });
return this;
}
/**
* An optimized implementation of toArray
*
* @returns {any[]}
* @memberof OrderedEnumerable
*/
toArray() {
const { startIndex, endIndex, arr } = this.getSortedArray();
return arr
? arr.slice(startIndex, endIndex)
: [];
}
/**
* An optimized implementation of toMap
*
* @param {ISelector} keySelector
* @param {ISelector} [valueSelector=x => x]
* @returns {Map<any, any>}
* @memberof OrderedEnumerable
*/
toMap(keySelector, valueSelector = x => x) {
Linqer._ensureFunction(keySelector);
Linqer._ensureFunction(valueSelector);
const result = new Map();
const arr = this.toArray();
for (let i = 0; i < arr.length; i++) {
result.set(keySelector(arr[i], i), valueSelector(arr[i], i));
}
return result;
}
/**
* An optimized implementation of toObject
*
* @param {ISelector} keySelector
* @param {ISelector} [valueSelector=x => x]
* @returns {{ [key: string]: any }}
* @memberof OrderedEnumerable
*/
toObject(keySelector, valueSelector = x => x) {
Linqer._ensureFunction(keySelector);
Linqer._ensureFunction(valueSelector);
const result = {};
const arr = this.toArray();
for (let i = 0; i < arr.length; i++) {
result[keySelector(arr[i], i)] = valueSelector(arr[i], i);
}
return result;
}
/**
* An optimized implementation of to Set
*
* @returns {Set<any>}
* @memberof OrderedEnumerable
*/
toSet() {
const result = new Set();
const arr = this.toArray();
for (let i = 0; i < arr.length; i++) {
result.add(arr[i]);
}
return result;
}
}
Linqer.OrderedEnumerable = OrderedEnumerable;
const _insertionSortThreshold = 64;
/// insertion sort is used for small intervals
function _insertionsort(arr, leftIndex, rightIndex, comparer) {
for (let j = leftIndex; j <= rightIndex; j++) {
const key = arr[j];
let i = j - 1;
while (i >= leftIndex && comparer(arr[i], key) > 0) {
arr[i + 1] = arr[i];
i--;
}
arr[i + 1] = key;
}
}
/// swap two items in an array by index
function _swapArrayItems(array, leftIndex, rightIndex) {
const temp = array[leftIndex];
array[leftIndex] = array[rightIndex];
array[rightIndex] = temp;
}
// Quicksort partition by center value coming from both sides
function _partition(items, left, right, comparer) {
const pivot = items[(right + left) >> 1];
while (left <= right) {
while (comparer(items[left], pivot) < 0) {
left++;
}
while (comparer(items[right], pivot) > 0) {
right--;
}
if (left < right) {
_swapArrayItems(items, left, right);
left++;
right--;
}
else {
if (left === right)
return left + 1;
}
}
return left;
}
/// optimized Quicksort algorithm
function _quickSort(items, left, right, comparer = Linqer._defaultComparer, minIndex = 0, maxIndex = Number.MAX_SAFE_INTEGER) {
if (!items.length)
return items;
// store partition indexes to be processed in here
const partitions = [];
partitions.push({ left, right });
let size = 1;
// the actual size of the partitions array never decreases
// but we keep score of the number of partitions in 'size'
// and we reuse slots whenever possible
while (size) {
const partition = { left, right } = partitions[size - 1];
if (right - left < _insertionSortThreshold) {
_insertionsort(items, left, right, comparer);
size--;
continue;
}
const index = _partition(items, left, right, comparer);
if (left < index - 1 && index - 1 >= minIndex) {
partition.right = index - 1;
if (index < right && index < maxIndex) {
partitions[size] = { left: index, right };
size++;
}
}
else {
if (index < right && index < maxIndex) {
partition.left = index;
}
else {
size--;
}
}
}
return items;
}
})(Linqer || (Linqer = {}));
/// <reference path="./LInQer.Slim.ts" />
/// <reference path="./LInQer.Enumerable.ts" />
/// <reference path="./LInQer.OrderedEnumerable.ts" />
var Linqer;
/// <reference path="./LInQer.Slim.ts" />
/// <reference path="./LInQer.Enumerable.ts" />
/// <reference path="./LInQer.OrderedEnumerable.ts" />
(function (Linqer) {
/// randomizes the enumerable (partial Fisher-Yates)
Linqer.Enumerable.prototype.shuffle = function () {
const self = this;
function* gen() {
const arr = self.toArray();
const len = arr.length;
let n = 0;
while (n < len) {
let k = n + Math.floor(Math.random() * (len - n));
const value = arr[k];
arr[k] = arr[n];
arr[n] = value;
n++;
yield value;
}
}
const result = Linqer.Enumerable.from(gen);
result._count = () => self.count();
return result;
};
/// implements random reservoir sampling of k items, with the option to specify a maximum limit for the items
Linqer.Enumerable.prototype.randomSample = function (k, limit = Number.MAX_SAFE_INTEGER) {
let index = 0;
const sample = [];
Linqer._ensureInternalTryGetAt(this);
if (this._canSeek) { // L algorithm
const length = this.count();
let index = 0;
for (index = 0; index < k && index < limit && index < length; index++) {
sample.push(this.elementAt(index));
}
let W = Math.exp(Math.log(Math.random()) / k);
while (index < length && index < limit) {
index += Math.floor(Math.log(Math.random()) / Math.log(1 - W)) + 1;
if (index < length && index < limit) {
sample[Math.floor(Math.random() * k)] = this.elementAt(index);
W *= Math.exp(Math.log(Math.random()) / k);
}
}
}
else { // R algorithm
for (const item of this) {
if (index < k) {
sample.push(item);
}
else {
const j = Math.floor(Math.random() * index);
if (j < k) {
sample[j] = item;
}
}
index++;
if (index >= limit)
break;
}
}
return Linqer.Enumerable.from(sample);
};
/// returns the distinct values based on a hashing function
Linqer.Enumerable.prototype.distinctByHash = function (hashFunc) {
// this is much more performant than distinct with a custom comparer
const self = this;
const gen = function* () {
const distinctValues = new Set();
for (const item of self) {
const size = distinctValues.size;
distinctValues.add(hashFunc(item));
if (size < distinctValues.size) {
yield item;
}
}
};
return new Linqer.Enumerable(gen);
};
/// returns the values that have different hashes from the items of the iterable provided
Linqer.Enumerable.prototype.exceptByHash = function (iterable, hashFunc) {
// this is much more performant than except with a custom comparer
Linqer._ensureIterable(iterable);
const self = this;
const gen = function* () {
const distinctValues = Linqer.Enumerable.from(iterable).select(hashFunc).toSet();
for (const item of self) {
if (!distinctValues.has(hashFunc(item))) {
yield item;
}
}
};
return new Linqer.Enumerable(gen);
};
/// returns the values that have the same hashes as items of the iterable provided
Linqer.Enumerable.prototype.intersectByHash = function (iterable, hashFunc) {
// this is much more performant than intersect with a custom comparer
Linqer._ensureIterable(iterable);
const self = this;
const gen = function* () {
const distinctValues = Linqer.Enumerable.from(iterable).select(hashFunc).toSet();
for (const item of self) {
if (distinctValues.has(hashFunc(item))) {
yield item;
}
}
};
return new Linqer.Enumerable(gen);
};
/// returns the index of a value in an ordered enumerable or false if not found
/// WARNING: use the same comparer as the one used in the ordered enumerable. The algorithm assumes the enumerable is already sorted.
Linqer.Enumerable.prototype.binarySearch = function (value, comparer = Linqer._defaultComparer) {
let enumerable = this.toList();
let start = 0;
let end = enumerable.count() - 1;
while (start <= end) {
const mid = (start + end) >> 1;
const comp = comparer(enumerable.elementAt(mid), value);
if (comp == 0)
return mid;
if (comp < 0) {
start = mid + 1;
}
else {
end = mid - 1;
}
}
return false;
};
/// joins each item of the enumerable with previous items from the same enumerable
Linqer.Enumerable.prototype.lag = function (offset, zipper) {
if (!offset) {
throw new Error('offset has to be positive');
}
if (offset < 0) {
throw new Error('offset has to be positive. Use .lead if you want to join with next items');
}
if (!zipper) {
zipper = (i1, i2) => [i1, i2];
}
else {
Linqer._ensureFunction(zipper);
}
const self = this;
Linqer._ensureInternalTryGetAt(this);
// generator uses a buffer to hold all the items within the offset interval
const gen = function* () {
const buffer = Array(offset);
let index = 0;
for (const item of self) {
const index2 = index - offset;
const item2 = index2 < 0
? undefined
: buffer[index2 % offset];
yield zipper(item, item2);
buffer[index % offset] = item;
index++;
}
};
const result = new Linqer.Enumerable(gen);
// count is the same as of the original enumerable
result._count = () => {
const count = self.count();
if (!result._wasIterated)
result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible only if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index) => {
const val1 = self._tryGetAt(index);
const val2 = self._tryGetAt(index - offset);
if (val1) {
return {
value: zipper(val1.value, val2 ? val2.value : undefined)
};
}
return null;
};
}
return result;
};
/// joins each item of the enumerable with next items from the same enumerable
Linqer.Enumerable.prototype.lead = function (offset, zipper) {
if (!offset) {
throw new Error('offset has to be positive');
}
if (offset < 0) {
throw new Error('offset has to be positive. Use .lag if you want to join with previous items');
}
if (!zipper) {
zipper = (i1, i2) => [i1, i2];
}
else {
Linqer._ensureFunction(zipper);
}
const self = this;
Linqer._ensureInternalTryGetAt(this);
// generator uses a buffer to hold all the items within the offset interval
const gen = function* () {
const buffer = Array(offset);
let index = 0;
for (const item of self) {
const index2 = index - offset;
if (index2 >= 0) {
const item2 = buffer[index2 % offset];
yield zipper(item2, item);
}
buffer[index % offset] = item;
index++;
}
for (let i = 0; i < offset; i++) {
const item = buffer[(index + i) % offset];
yield zipper(item, undefined);
}
};
const result = new Linqer.Enumerable(gen);
// count is the same as of the original enumerable
result._count = () => {
const count = self.count();
if (!result._wasIterated)
result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible only if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index) => {
const val1 = self._tryGetAt(index);
const val2 = self._tryGetAt(index + offset);
if (val1) {
return {
value: zipper(val1.value, val2 ? val2.value : undefined)
};
}
return null;
};
}
return result;
};
/// returns an enumerable of at least minLength, padding the end with a value or the result of a function
Linqer.Enumerable.prototype.padEnd = function (minLength, filler) {
if (minLength <= 0) {
throw new Error('minLength has to be positive.');
}
let fillerFunc;
if (typeof filler !== 'function') {
fillerFunc = (index) => filler;
}
else {
fillerFunc = filler;
}
const self = this;
Linqer._ensureInternalTryGetAt(this);
// generator iterates all elements,
// then yields the result of the filler function until minLength items
const gen = function* () {
let index = 0;
for (const item of self) {
yield item;
index++;
}
for (; index < minLength; index++) {
yield fillerFunc(index);
}
};
const result = new Linqer.Enumerable(gen);
// count is the maximum between minLength and the original count
result._count = () => {
const count = Math.max(minLength, self.count());
if (!result._wasIterated)
result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index) => {
const val = self._tryGetAt(index);
if (val)
return val;
if (index < minLength) {
return { value: fillerFunc(index) };
}
return null;
};
}
return result;
};
/// returns an enumerable of at least minLength, padding the start with a value or the result of a function
/// if the enumerable cannot seek, then it will be iterated minLength time
Linqer.Enumerable.prototype.padStart = function (minLength, filler) {
if (minLength <= 0) {
throw new Error('minLength has to be positive.');
}
let fillerFunc;
if (typeof filler !== 'function') {
fillerFunc = (index) => filler;
}
else {
fillerFunc = filler;
}
const self = this;
Linqer._ensureInternalTryGetAt(self);
// generator needs a buffer to hold offset values
// it yields values from the buffer when it overflows
// or filler function results if the buffer is not full
// after iterating the entire original enumerable
const gen = function* () {
const buffer = Array(minLength);
let index = 0;
const iterator = self[Symbol.iterator]();
let flushed = false;
let done = false;
do {
const val = iterator.next();
done = !!val.done;
if (!done) {
buffer[index] = val.value;
index++;
}
if (flushed && !done) {
yield val.value;
}
else {
if (done || index === minLength) {
for (let i = 0; i < minLength - index; i++) {
yield fillerFunc(i);
}
for (let i = 0; i < index; i++) {
yield buffer[i];
}
flushed = true;
}
}
} while (!done);
};
const result = new Linqer.Enumerable(gen);
// count is the max of minLength and the original count
result._count = () => {
const count = Math.max(minLength, self.count());
if (!result._wasIterated)
result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible only if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index) => {
const count = self.count();
const delta = minLength - count;
if (delta <= 0) {
return self._tryGetAt(index);
}
if (index < delta) {
return { value: fillerFunc(index) };
}
return self._tryGetAt(index - delta);
};
}
return result;
};
})(Linqer || (Linqer = {}));
// export to NPM
if (typeof (module) !== 'undefined') {
module.exports = Linqer;
}
//# sourceMappingURL=LInQer.all.js.map
================================================
FILE: LInQer.extra.js
================================================
"use strict";
/// <reference path="./LInQer.Slim.ts" />
/// <reference path="./LInQer.Enumerable.ts" />
/// <reference path="./LInQer.OrderedEnumerable.ts" />
var Linqer;
/// <reference path="./LInQer.Slim.ts" />
/// <reference path="./LInQer.Enumerable.ts" />
/// <reference path="./LInQer.OrderedEnumerable.ts" />
(function (Linqer) {
/// randomizes the enumerable (partial Fisher-Yates)
Linqer.Enumerable.prototype.shuffle = function () {
const self = this;
function* gen() {
const arr = self.toArray();
const len = arr.length;
let n = 0;
while (n < len) {
let k = n + Math.floor(Math.random() * (len - n));
const value = arr[k];
arr[k] = arr[n];
arr[n] = value;
n++;
yield value;
}
}
const result = Linqer.Enumerable.from(gen);
result._count = () => self.count();
return result;
};
/// implements random reservoir sampling of k items, with the option to specify a maximum limit for the items
Linqer.Enumerable.prototype.randomSample = function (k, limit = Number.MAX_SAFE_INTEGER) {
let index = 0;
const sample = [];
Linqer._ensureInternalTryGetAt(this);
if (this._canSeek) { // L algorithm
const length = this.count();
let index = 0;
for (index = 0; index < k && index < limit && index < length; index++) {
sample.push(this.elementAt(index));
}
let W = Math.exp(Math.log(Math.random()) / k);
while (index < length && index < limit) {
index += Math.floor(Math.log(Math.random()) / Math.log(1 - W)) + 1;
if (index < length && index < limit) {
sample[Math.floor(Math.random() * k)] = this.elementAt(index);
W *= Math.exp(Math.log(Math.random()) / k);
}
}
}
else { // R algorithm
for (const item of this) {
if (index < k) {
sample.push(item);
}
else {
const j = Math.floor(Math.random() * index);
if (j < k) {
sample[j] = item;
}
}
index++;
if (index >= limit)
break;
}
}
return Linqer.Enumerable.from(sample);
};
/// returns the distinct values based on a hashing function
Linqer.Enumerable.prototype.distinctByHash = function (hashFunc) {
// this is much more performant than distinct with a custom comparer
const self = this;
const gen = function* () {
const distinctValues = new Set();
for (const item of self) {
const size = distinctValues.size;
distinctValues.add(hashFunc(item));
if (size < distinctValues.size) {
yield item;
}
}
};
return new Linqer.Enumerable(gen);
};
/// returns the values that have different hashes from the items of the iterable provided
Linqer.Enumerable.prototype.exceptByHash = function (iterable, hashFunc) {
// this is much more performant than except with a custom comparer
Linqer._ensureIterable(iterable);
const self = this;
const gen = function* () {
const distinctValues = Linqer.Enumerable.from(iterable).select(hashFunc).toSet();
for (const item of self) {
if (!distinctValues.has(hashFunc(item))) {
yield item;
}
}
};
return new Linqer.Enumerable(gen);
};
/// returns the values that have the same hashes as items of the iterable provided
Linqer.Enumerable.prototype.intersectByHash = function (iterable, hashFunc) {
// this is much more performant than intersect with a custom comparer
Linqer._ensureIterable(iterable);
const self = this;
const gen = function* () {
const distinctValues = Linqer.Enumerable.from(iterable).select(hashFunc).toSet();
for (const item of self) {
if (distinctValues.has(hashFunc(item))) {
yield item;
}
}
};
return new Linqer.Enumerable(gen);
};
/// returns the index of a value in an ordered enumerable or false if not found
/// WARNING: use the same comparer as the one used in the ordered enumerable. The algorithm assumes the enumerable is already sorted.
Linqer.Enumerable.prototype.binarySearch = function (value, comparer = Linqer._defaultComparer) {
let enumerable = this.toList();
let start = 0;
let end = enumerable.count() - 1;
while (start <= end) {
const mid = (start + end) >> 1;
const comp = comparer(enumerable.elementAt(mid), value);
if (comp == 0)
return mid;
if (comp < 0) {
start = mid + 1;
}
else {
end = mid - 1;
}
}
return false;
};
/// joins each item of the enumerable with previous items from the same enumerable
Linqer.Enumerable.prototype.lag = function (offset, zipper) {
if (!offset) {
throw new Error('offset has to be positive');
}
if (offset < 0) {
throw new Error('offset has to be positive. Use .lead if you want to join with next items');
}
if (!zipper) {
zipper = (i1, i2) => [i1, i2];
}
else {
Linqer._ensureFunction(zipper);
}
const self = this;
Linqer._ensureInternalTryGetAt(this);
// generator uses a buffer to hold all the items within the offset interval
const gen = function* () {
const buffer = Array(offset);
let index = 0;
for (const item of self) {
const index2 = index - offset;
const item2 = index2 < 0
? undefined
: buffer[index2 % offset];
yield zipper(item, item2);
buffer[index % offset] = item;
index++;
}
};
const result = new Linqer.Enumerable(gen);
// count is the same as of the original enumerable
result._count = () => {
const count = self.count();
if (!result._wasIterated)
result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible only if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index) => {
const val1 = self._tryGetAt(index);
const val2 = self._tryGetAt(index - offset);
if (val1) {
return {
value: zipper(val1.value, val2 ? val2.value : undefined)
};
}
return null;
};
}
return result;
};
/// joins each item of the enumerable with next items from the same enumerable
Linqer.Enumerable.prototype.lead = function (offset, zipper) {
if (!offset) {
throw new Error('offset has to be positive');
}
if (offset < 0) {
throw new Error('offset has to be positive. Use .lag if you want to join with previous items');
}
if (!zipper) {
zipper = (i1, i2) => [i1, i2];
}
else {
Linqer._ensureFunction(zipper);
}
const self = this;
Linqer._ensureInternalTryGetAt(this);
// generator uses a buffer to hold all the items within the offset interval
const gen = function* () {
const buffer = Array(offset);
let index = 0;
for (const item of self) {
const index2 = index - offset;
if (index2 >= 0) {
const item2 = buffer[index2 % offset];
yield zipper(item2, item);
}
buffer[index % offset] = item;
index++;
}
for (let i = 0; i < offset; i++) {
const item = buffer[(index + i) % offset];
yield zipper(item, undefined);
}
};
const result = new Linqer.Enumerable(gen);
// count is the same as of the original enumerable
result._count = () => {
const count = self.count();
if (!result._wasIterated)
result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible only if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index) => {
const val1 = self._tryGetAt(index);
const val2 = self._tryGetAt(index + offset);
if (val1) {
return {
value: zipper(val1.value, val2 ? val2.value : undefined)
};
}
return null;
};
}
return result;
};
/// returns an enumerable of at least minLength, padding the end with a value or the result of a function
Linqer.Enumerable.prototype.padEnd = function (minLength, filler) {
if (minLength <= 0) {
throw new Error('minLength has to be positive.');
}
let fillerFunc;
if (typeof filler !== 'function') {
fillerFunc = (index) => filler;
}
else {
fillerFunc = filler;
}
const self = this;
Linqer._ensureInternalTryGetAt(this);
// generator iterates all elements,
// then yields the result of the filler function until minLength items
const gen = function* () {
let index = 0;
for (const item of self) {
yield item;
index++;
}
for (; index < minLength; index++) {
yield fillerFunc(index);
}
};
const result = new Linqer.Enumerable(gen);
// count is the maximum between minLength and the original count
result._count = () => {
const count = Math.max(minLength, self.count());
if (!result._wasIterated)
result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index) => {
const val = self._tryGetAt(index);
if (val)
return val;
if (index < minLength) {
return { value: fillerFunc(index) };
}
return null;
};
}
return result;
};
/// returns an enumerable of at least minLength, padding the start with a value or the result of a function
/// if the enumerable cannot seek, then it will be iterated minLength time
Linqer.Enumerable.prototype.padStart = function (minLength, filler) {
if (minLength <= 0) {
throw new Error('minLength has to be positive.');
}
let fillerFunc;
if (typeof filler !== 'function') {
fillerFunc = (index) => filler;
}
else {
fillerFunc = filler;
}
const self = this;
Linqer._ensureInternalTryGetAt(self);
// generator needs a buffer to hold offset values
// it yields values from the buffer when it overflows
// or filler function results if the buffer is not full
// after iterating the entire original enumerable
const gen = function* () {
const buffer = Array(minLength);
let index = 0;
const iterator = self[Symbol.iterator]();
let flushed = false;
let done = false;
do {
const val = iterator.next();
done = !!val.done;
if (!done) {
buffer[index] = val.value;
index++;
}
if (flushed && !done) {
yield val.value;
}
else {
if (done || index === minLength) {
for (let i = 0; i < minLength - index; i++) {
yield fillerFunc(i);
}
for (let i = 0; i < index; i++) {
yield buffer[i];
}
flushed = true;
}
}
} while (!done);
};
const result = new Linqer.Enumerable(gen);
// count is the max of minLength and the original count
result._count = () => {
const count = Math.max(minLength, self.count());
if (!result._wasIterated)
result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible only if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index) => {
const count = self.count();
const delta = minLength - count;
if (delta <= 0) {
return self._tryGetAt(index);
}
if (index < delta) {
return { value: fillerFunc(index) };
}
return self._tryGetAt(index - delta);
};
}
return result;
};
})(Linqer || (Linqer = {}));
//# sourceMappingURL=LInQer.extra.js.map
================================================
FILE: LInQer.extra.ts
================================================
/// <reference path="./LInQer.Slim.ts" />
/// <reference path="./LInQer.Enumerable.ts" />
/// <reference path="./LInQer.OrderedEnumerable.ts" />
namespace Linqer {
export interface Enumerable extends Iterable<any> {
/**
* Returns a randomized sequence of items from an initial source
* @returns shuffle
*/
shuffle(): Enumerable;
/**
* implements random reservoir sampling of k items, with the option to specify a maximum limit for the items
* @param k
* @param limit
* @returns sample
*/
randomSample(k: number, limit: number): Enumerable;
/**
* Returns the count of the items in a sequence. Depending on the sequence type this iterates through it or not.
* @returns count
*/
count(): number;
/**
* returns the distinct values based on a hashing function
*
* @param {ISelector} hashFunc
* @returns {Enumerable}
* @memberof Enumerable
*/
distinctByHash(hashFunc: ISelector): Enumerable;
/**
* returns the values that have different hashes from the items of the iterable provided
*
* @param {IterableType} iterable
* @param {ISelector} hashFunc
* @returns {Enumerable}
* @memberof Enumerable
*/
exceptByHash(iterable: IterableType, hashFunc: ISelector): Enumerable;
/**
* returns the values that have the same hashes as items of the iterable provided
*
* @param {IterableType} iterable
* @param {ISelector} hashFunc
* @returns {Enumerable}
* @memberof Enumerable
*/
intersectByHash(iterable: IterableType, hashFunc: ISelector): Enumerable;
/**
* returns the index of a value in an ordered enumerable or false if not found
* WARNING: use the same comparer as the one used to order the enumerable. The algorithm assumes the enumerable is already sorted.
*
* @param {*} value
* @param {IComparer} comparer
* @returns {(number | boolean)}
* @memberof Enumerable
*/
binarySearch(value: any, comparer: IComparer): number | boolean;
/**
* joins each item of the enumerable with previous items from the same enumerable
* @param offset
* @param zipper
* @returns lag
*/
lag(offset: number, zipper: (item1: any, item2: any) => any): Enumerable;
/**
* joins each item of the enumerable with next items from the same enumerable
*
* @param {number} offset
* @param {(item1: any, item2: any) => any} zipper
* @returns {Enumerable}
* @memberof Enumerable
*/
lead(offset: number, zipper: (item1: any, item2: any) => any): Enumerable;
/**
* returns an enumerable of at least minLength, padding the end with a value or the result of a function
*
* @param {number} minLength
* @param {(any | ((index: number) => any))} filler
* @returns {Enumerable}
* @memberof Enumerable
*/
padEnd(minLength: number, filler: any | ((index: number) => any)): Enumerable;
/**
* returns an enumerable of at least minLength, padding the start with a value or the result of a function
* if the enumerable cannot seek, then it will be iterated minLength time
*
* @param {number} minLength
* @param {(any | ((index: number) => any))} filler
* @returns {Enumerable}
* @memberof Enumerable
*/
padStart(minLength: number, filler: any | ((index: number) => any)): Enumerable;
}
/// randomizes the enumerable (partial Fisher-Yates)
Enumerable.prototype.shuffle = function (): Enumerable {
const self = this;
function* gen() {
const arr = self.toArray();
const len = arr.length;
let n = 0;
while (n < len) {
let k = n + Math.floor(Math.random() * (len - n));
const value = arr[k];
arr[k] = arr[n];
arr[n] = value;
n++;
yield value;
}
}
const result = Enumerable.from(gen);
result._count = () => self.count();
return result;
};
/// implements random reservoir sampling of k items, with the option to specify a maximum limit for the items
Enumerable.prototype.randomSample = function (k: number, limit: number = Number.MAX_SAFE_INTEGER): Enumerable {
let index = 0;
const sample = [];
_ensureInternalTryGetAt(this);
if (this._canSeek) { // L algorithm
const length = this.count();
let index = 0;
for (index = 0; index < k && index < limit && index < length; index++) {
sample.push(this.elementAt(index));
}
let W = Math.exp(Math.log(Math.random()) / k);
while (index < length && index < limit) {
index += Math.floor(Math.log(Math.random()) / Math.log(1 - W)) + 1;
if (index < length && index < limit) {
sample[Math.floor(Math.random() * k)] = this.elementAt(index);
W *= Math.exp(Math.log(Math.random()) / k);
}
}
} else { // R algorithm
for (const item of this) {
if (index < k) {
sample.push(item);
} else {
const j = Math.floor(Math.random() * index);
if (j < k) {
sample[j] = item;
}
}
index++;
if (index >= limit) break;
}
}
return Enumerable.from(sample);
}
/// returns the distinct values based on a hashing function
Enumerable.prototype.distinctByHash = function (hashFunc: ISelector): Enumerable {
// this is much more performant than distinct with a custom comparer
const self = this;
const gen = function* () {
const distinctValues = new Set();
for (const item of self) {
const size = distinctValues.size;
distinctValues.add(hashFunc(item));
if (size < distinctValues.size) {
yield item;
}
}
};
return new Enumerable(gen);
};
/// returns the values that have different hashes from the items of the iterable provided
Enumerable.prototype.exceptByHash = function (iterable: IterableType, hashFunc: ISelector): Enumerable {
// this is much more performant than except with a custom comparer
_ensureIterable(iterable);
const self = this;
const gen = function* () {
const distinctValues = Enumerable.from(iterable).select(hashFunc).toSet();
for (const item of self) {
if (!distinctValues.has(hashFunc(item))) {
yield item;
}
}
};
return new Enumerable(gen);
};
/// returns the values that have the same hashes as items of the iterable provided
Enumerable.prototype.intersectByHash = function (iterable: IterableType, hashFunc: ISelector): Enumerable {
// this is much more performant than intersect with a custom comparer
_ensureIterable(iterable);
const self = this;
const gen = function* () {
const distinctValues = Enumerable.from(iterable).select(hashFunc).toSet();
for (const item of self) {
if (distinctValues.has(hashFunc(item))) {
yield item;
}
}
};
return new Enumerable(gen);
};
/// returns the index of a value in an ordered enumerable or false if not found
/// WARNING: use the same comparer as the one used in the ordered enumerable. The algorithm assumes the enumerable is already sorted.
Enumerable.prototype.binarySearch = function (value: any, comparer: IComparer = _defaultComparer): number | boolean {
let enumerable: Enumerable = this.toList();
let start = 0;
let end = enumerable.count() - 1;
while (start <= end) {
const mid = (start + end) >> 1;
const comp = comparer(enumerable.elementAt(mid), value);
if (comp == 0) return mid;
if (comp < 0) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return false;
};
/// joins each item of the enumerable with previous items from the same enumerable
Enumerable.prototype.lag = function (offset: number, zipper: (item1: any, item2: any) => any): Enumerable {
if (!offset) {
throw new Error('offset has to be positive');
}
if (offset < 0) {
throw new Error('offset has to be positive. Use .lead if you want to join with next items');
}
if (!zipper) {
zipper = (i1, i2) => [i1, i2];
} else {
_ensureFunction(zipper);
}
const self = this;
_ensureInternalTryGetAt(this);
// generator uses a buffer to hold all the items within the offset interval
const gen = function* () {
const buffer = Array(offset);
let index = 0;
for (const item of self) {
const index2 = index - offset;
const item2 = index2 < 0
? undefined
: buffer[index2 % offset];
yield zipper(item, item2);
buffer[index % offset] = item;
index++;
}
};
const result = new Enumerable(gen);
// count is the same as of the original enumerable
result._count = () => {
const count = self.count();
if (!result._wasIterated) result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible only if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index: number) => {
const val1 = self._tryGetAt!(index);
const val2 = self._tryGetAt!(index - offset);
if (val1) {
return {
value: zipper(
val1.value,
val2 ? val2.value : undefined
)
};
}
return null;
};
}
return result;
}
/// joins each item of the enumerable with next items from the same enumerable
Enumerable.prototype.lead = function (offset: number, zipper: (item1: any, item2: any) => any): Enumerable {
if (!offset) {
throw new Error('offset has to be positive');
}
if (offset < 0) {
throw new Error('offset has to be positive. Use .lag if you want to join with previous items');
}
if (!zipper) {
zipper = (i1, i2) => [i1, i2];
} else {
_ensureFunction(zipper);
}
const self = this;
_ensureInternalTryGetAt(this);
// generator uses a buffer to hold all the items within the offset interval
const gen = function* () {
const buffer = Array(offset);
let index = 0;
for (const item of self) {
const index2 = index - offset;
if (index2 >= 0) {
const item2 = buffer[index2 % offset];
yield zipper(item2, item);
}
buffer[index % offset] = item;
index++;
}
for (let i = 0; i < offset; i++) {
const item = buffer[(index + i) % offset];
yield zipper(item, undefined);
}
};
const result = new Enumerable(gen);
// count is the same as of the original enumerable
result._count = () => {
const count = self.count();
if (!result._wasIterated) result._wasIterated = self._wasIterated;
return count;
};
// seeking is possible only if the original was seekable
if (self._canSeek) {
result._canSeek = true;
result._tryGetAt = (index: number) => {
const val1 = self._tryGetAt!(index);
const val2 = self._tryGetAt!(index + offset);
if (val1) {
return {
value: zipper(
val1.value,
val2 ? val2.value : undefined
)
};
}
return null;
};
}
return result;
}
/// returns an enumerable of at least minLength, padding the end with a value or the result of a function
Enumerable.prototype.padEnd = function (minLength: number, filler: any | ((index: number) => any)): Enumerable {
if (minLength <= 0) {
throw new Error('minLength has to be positive.');
}
let fillerFunc: (index: number) => any;
if (typeof filler !== 'function') {
fillerFunc = (index: number) => filler;
} else {
fillerFunc = filler;
}
const self = this;
_ensureInternalTryGetAt(this);
// generator iterates all elements,
gitextract_vvtgv5z_/ ├── .gitignore ├── .vscode/ │ └── settings.json ├── LICENSE ├── LInQer.Enumerable.ts ├── LInQer.GroupEnumerable.ts ├── LInQer.OrderedEnumerable.ts ├── LInQer.Slim.ts ├── LInQer.all.d.ts ├── LInQer.all.js ├── LInQer.extra.js ├── LInQer.extra.ts ├── LInQer.js ├── LInQer.slim.js ├── README.md ├── Release.txt ├── build.bat ├── npm.export.ts ├── package.json ├── qUnit/ │ ├── qunit-2.9.2.css │ └── qunit-2.9.2.js ├── tests.html ├── tests.js ├── tests.performance.html ├── tests.performance.js ├── tests.slim.html ├── tests.slim.js ├── tsconfig.all.json ├── tsconfig.extra.json ├── tsconfig.json └── tsconfig.slim.json
SYMBOL INDEX (379 symbols across 10 files)
FILE: LInQer.Enumerable.ts
type Enumerable (line 5) | interface Enumerable extends Iterable<any> {
FILE: LInQer.GroupEnumerable.ts
type Enumerable (line 5) | interface Enumerable extends Iterable<any> {
class GroupEnumerable (line 171) | class GroupEnumerable extends Enumerable {
method constructor (line 173) | constructor(iterable: IterableType, key: string) {
FILE: LInQer.OrderedEnumerable.ts
type Enumerable (line 4) | interface Enumerable extends Iterable<any> {
type RestrictionType (line 77) | enum RestrictionType {
class OrderedEnumerable (line 91) | class OrderedEnumerable extends Enumerable {
method constructor (line 102) | constructor(src: IterableType,
method getSortedArray (line 135) | private getSortedArray() {
method generateSortFunc (line 173) | private generateSortFunc(selectors: { keySelector: ISelector, ascendin...
method getStartAndEndIndexes (line 202) | private getStartAndEndIndexes(restrictions: { type: RestrictionType, n...
method thenBy (line 232) | thenBy(keySelector: ISelector): OrderedEnumerable {
method thenByDescending (line 243) | thenByDescending(keySelector: ISelector): OrderedEnumerable {
method take (line 255) | take(nr: number): OrderedEnumerable {
method takeLast (line 267) | takeLast(nr: number): OrderedEnumerable {
method skip (line 279) | skip(nr: number): OrderedEnumerable {
method skipLast (line 291) | skipLast(nr: number): OrderedEnumerable {
method toArray (line 303) | toArray(): any[] {
method toMap (line 319) | toMap(keySelector: ISelector, valueSelector: ISelector = x => x): Map<...
method toObject (line 339) | toObject(keySelector: ISelector, valueSelector: ISelector = x => x): {...
method toSet (line 357) | toSet(): Set<any> {
function _insertionsort (line 371) | function _insertionsort(arr: any[], leftIndex: number, rightIndex: numbe...
function _swapArrayItems (line 384) | function _swapArrayItems(array: any[], leftIndex: number, rightIndex: nu...
function _partition (line 390) | function _partition(items: any[], left: number, right: number, comparer:...
function _quickSort (line 411) | function _quickSort(items: any[], left: number, right: number, comparer:...
FILE: LInQer.Slim.ts
class Enumerable (line 11) | class Enumerable implements Iterable<any>, IUsesQuickSort {
method constructor (line 35) | constructor(src: IterableType) {
method from (line 65) | static from(iterable: IterableType): Enumerable {
method empty (line 88) | static empty(): Enumerable {
method range (line 105) | static range(start: number, count: number): Enumerable {
method repeat (line 130) | static repeat(item: any, count: number): Enumerable {
method length (line 149) | get length():number {
method concat (line 162) | concat(iterable: IterableType): Enumerable {
method count (line 197) | count(): number {
method distinct (line 211) | distinct(equalityComparer: IEqualityComparer = EqualityComparer.defaul...
method elementAt (line 252) | elementAt(index: number): any {
method elementAtOrDefault (line 267) | elementAtOrDefault(index: number): any | undefined {
method first (line 281) | first(): any {
method firstOrDefault (line 292) | firstOrDefault(): any | undefined {
method last (line 303) | last(): any {
method lastOrDefault (line 328) | lastOrDefault(): any | undefined {
method stats (line 349) | stats(comparer?: IComparer): { count: number, min: any, max: any } {
method min (line 376) | min(comparer?: IComparer): any {
method max (line 392) | max(comparer?: IComparer): any {
method select (line 407) | select(selector: ISelector): Enumerable {
method skip (line 441) | skip(nr: number): Enumerable {
method splice (line 474) | splice(start: number, howmany: number, ...newItems:any[]) : Enumerable {
method sum (line 486) | sum(): number | undefined {
method sumAndCount (line 500) | sumAndCount(): { sum: number, count: number } {
method take (line 522) | take(nr: number): Enumerable {
method toArray (line 560) | toArray(): any[] {
method toList (line 593) | toList(): Enumerable {
method where (line 606) | where(condition: IFilter): Enumerable {
method [Symbol.iterator] (line 76) | [Symbol.iterator](): Iterator<any> {
function _ensureIterable (line 625) | function _ensureIterable(src: IterableType): void {
function _ensureFunction (line 633) | function _ensureFunction(f: Function): void {
function _toNumber (line 638) | function _toNumber(obj: any): number {
function _toArray (line 644) | function _toArray(iterable: IterableType) {
function _ensureInternalCount (line 650) | function _ensureInternalCount(enumerable: Enumerable) {
function _ensureInternalTryGetAt (line 678) | function _ensureInternalTryGetAt(enumerable: Enumerable) {
type IterableType (line 736) | type IterableType = Iterable<any> | (() => Iterator<any>) | Enumerable;
type IComparer (line 741) | type IComparer = (item1: any, item2: any) => -1 | 0 | 1;
type ISelector (line 745) | type ISelector<T = any> = (item: any, index?: number) => T;
type IFilter (line 749) | type IFilter = ISelector<boolean>;
type IEqualityComparer (line 765) | type IEqualityComparer = (item1: any, item2: any) => boolean;
type IUsesQuickSort (line 779) | interface IUsesQuickSort {
FILE: LInQer.all.d.ts
class Enumerable (line 10) | class Enumerable implements Iterable<any>, IUsesQuickSort {
type IterableType (line 254) | type IterableType = Iterable<any> | (() => Iterator<any>) | Enumerable;
type IComparer (line 258) | type IComparer = (item1: any, item2: any) => -1 | 0 | 1;
type ISelector (line 262) | type ISelector<T = any> = (item: any, index?: number) => T;
type IFilter (line 266) | type IFilter = ISelector<boolean>;
type IEqualityComparer (line 276) | type IEqualityComparer = (item1: any, item2: any) => boolean;
type IUsesQuickSort (line 286) | interface IUsesQuickSort {
type Enumerable (line 292) | interface Enumerable extends Iterable<any> {
type Enumerable (line 530) | interface Enumerable extends Iterable<any> {
class GroupEnumerable (line 574) | class GroupEnumerable extends Enumerable {
type Enumerable (line 580) | interface Enumerable extends Iterable<any> {
type RestrictionType (line 612) | enum RestrictionType {
class OrderedEnumerable (line 625) | class OrderedEnumerable extends Enumerable {
type Enumerable (line 731) | interface Enumerable extends Iterable<any> {
FILE: LInQer.all.js
class Enumerable (line 12) | class Enumerable {
method constructor (line 18) | constructor(src) {
method from (line 48) | static from(iterable) {
method empty (line 70) | static empty() {
method range (line 86) | static range(start, count) {
method repeat (line 111) | static repeat(item, count) {
method length (line 130) | get length() {
method concat (line 143) | concat(iterable) {
method count (line 176) | count() {
method distinct (line 188) | distinct(equalityComparer = Linqer.EqualityComparer.default) {
method elementAt (line 228) | elementAt(index) {
method elementAtOrDefault (line 242) | elementAtOrDefault(index) {
method first (line 255) | first() {
method firstOrDefault (line 264) | firstOrDefault() {
method last (line 273) | last() {
method lastOrDefault (line 297) | lastOrDefault() {
method stats (line 317) | stats(comparer) {
method min (line 346) | min(comparer) {
method max (line 360) | max(comparer) {
method select (line 373) | select(selector) {
method skip (line 406) | skip(nr) {
method splice (line 437) | splice(start, howmany, ...newItems) {
method sum (line 448) | sum() {
method sumAndCount (line 460) | sumAndCount() {
method take (line 480) | take(nr) {
method toArray (line 516) | toArray() {
method toList (line 548) | toList() {
method where (line 561) | where(condition) {
method [Symbol.iterator] (line 59) | [Symbol.iterator]() {
function _ensureIterable (line 580) | function _ensureIterable(src) {
function _ensureFunction (line 591) | function _ensureFunction(f) {
function _toNumber (line 598) | function _toNumber(obj) {
function _toArray (line 604) | function _toArray(iterable) {
function _ensureInternalCount (line 613) | function _ensureInternalCount(enumerable) {
function _ensureInternalTryGetAt (line 644) | function _ensureInternalTryGetAt(enumerable) {
class GroupEnumerable (line 1284) | class GroupEnumerable extends Linqer.Enumerable {
method constructor (line 1285) | constructor(iterable, key) {
class OrderedEnumerable (line 1345) | class OrderedEnumerable extends Linqer.Enumerable {
method constructor (line 1353) | constructor(src, keySelector, ascending = true) {
method getSortedArray (line 1382) | getSortedArray() {
method generateSortFunc (line 1421) | generateSortFunc(selectors) {
method getStartAndEndIndexes (line 1452) | getStartAndEndIndexes(restrictions, arrLength) {
method thenBy (line 1480) | thenBy(keySelector) {
method thenByDescending (line 1491) | thenByDescending(keySelector) {
method take (line 1502) | take(nr) {
method takeLast (line 1513) | takeLast(nr) {
method skip (line 1524) | skip(nr) {
method skipLast (line 1535) | skipLast(nr) {
method toArray (line 1545) | toArray() {
method toMap (line 1559) | toMap(keySelector, valueSelector = x => x) {
method toObject (line 1577) | toObject(keySelector, valueSelector = x => x) {
method toSet (line 1593) | toSet() {
function _insertionsort (line 1605) | function _insertionsort(arr, leftIndex, rightIndex, comparer) {
function _swapArrayItems (line 1617) | function _swapArrayItems(array, leftIndex, rightIndex) {
function _partition (line 1623) | function _partition(items, left, right, comparer) {
function _quickSort (line 1645) | function _quickSort(items, left, right, comparer = Linqer._defaultCompar...
FILE: LInQer.extra.ts
type Enumerable (line 7) | interface Enumerable extends Iterable<any> {
FILE: LInQer.js
class Enumerable (line 12) | class Enumerable {
method constructor (line 18) | constructor(src) {
method from (line 48) | static from(iterable) {
method empty (line 70) | static empty() {
method range (line 86) | static range(start, count) {
method repeat (line 111) | static repeat(item, count) {
method length (line 130) | get length() {
method concat (line 143) | concat(iterable) {
method count (line 176) | count() {
method distinct (line 188) | distinct(equalityComparer = Linqer.EqualityComparer.default) {
method elementAt (line 228) | elementAt(index) {
method elementAtOrDefault (line 242) | elementAtOrDefault(index) {
method first (line 255) | first() {
method firstOrDefault (line 264) | firstOrDefault() {
method last (line 273) | last() {
method lastOrDefault (line 297) | lastOrDefault() {
method stats (line 317) | stats(comparer) {
method min (line 346) | min(comparer) {
method max (line 360) | max(comparer) {
method select (line 373) | select(selector) {
method skip (line 406) | skip(nr) {
method splice (line 437) | splice(start, howmany, ...newItems) {
method sum (line 448) | sum() {
method sumAndCount (line 460) | sumAndCount() {
method take (line 480) | take(nr) {
method toArray (line 516) | toArray() {
method toList (line 548) | toList() {
method where (line 561) | where(condition) {
method [Symbol.iterator] (line 59) | [Symbol.iterator]() {
function _ensureIterable (line 580) | function _ensureIterable(src) {
function _ensureFunction (line 591) | function _ensureFunction(f) {
function _toNumber (line 598) | function _toNumber(obj) {
function _toArray (line 604) | function _toArray(iterable) {
function _ensureInternalCount (line 613) | function _ensureInternalCount(enumerable) {
function _ensureInternalTryGetAt (line 644) | function _ensureInternalTryGetAt(enumerable) {
class GroupEnumerable (line 1284) | class GroupEnumerable extends Linqer.Enumerable {
method constructor (line 1285) | constructor(iterable, key) {
class OrderedEnumerable (line 1345) | class OrderedEnumerable extends Linqer.Enumerable {
method constructor (line 1353) | constructor(src, keySelector, ascending = true) {
method getSortedArray (line 1382) | getSortedArray() {
method generateSortFunc (line 1421) | generateSortFunc(selectors) {
method getStartAndEndIndexes (line 1452) | getStartAndEndIndexes(restrictions, arrLength) {
method thenBy (line 1480) | thenBy(keySelector) {
method thenByDescending (line 1491) | thenByDescending(keySelector) {
method take (line 1502) | take(nr) {
method takeLast (line 1513) | takeLast(nr) {
method skip (line 1524) | skip(nr) {
method skipLast (line 1535) | skipLast(nr) {
method toArray (line 1545) | toArray() {
method toMap (line 1559) | toMap(keySelector, valueSelector = x => x) {
method toObject (line 1577) | toObject(keySelector, valueSelector = x => x) {
method toSet (line 1593) | toSet() {
function _insertionsort (line 1605) | function _insertionsort(arr, leftIndex, rightIndex, comparer) {
function _swapArrayItems (line 1617) | function _swapArrayItems(array, leftIndex, rightIndex) {
function _partition (line 1623) | function _partition(items, left, right, comparer) {
function _quickSort (line 1645) | function _quickSort(items, left, right, comparer = Linqer._defaultCompar...
FILE: LInQer.slim.js
class Enumerable (line 12) | class Enumerable {
method constructor (line 18) | constructor(src) {
method from (line 48) | static from(iterable) {
method empty (line 70) | static empty() {
method range (line 86) | static range(start, count) {
method repeat (line 111) | static repeat(item, count) {
method length (line 130) | get length() {
method concat (line 143) | concat(iterable) {
method count (line 176) | count() {
method distinct (line 188) | distinct(equalityComparer = Linqer.EqualityComparer.default) {
method elementAt (line 228) | elementAt(index) {
method elementAtOrDefault (line 242) | elementAtOrDefault(index) {
method first (line 255) | first() {
method firstOrDefault (line 264) | firstOrDefault() {
method last (line 273) | last() {
method lastOrDefault (line 297) | lastOrDefault() {
method stats (line 317) | stats(comparer) {
method min (line 346) | min(comparer) {
method max (line 360) | max(comparer) {
method select (line 373) | select(selector) {
method skip (line 406) | skip(nr) {
method splice (line 437) | splice(start, howmany, ...newItems) {
method sum (line 448) | sum() {
method sumAndCount (line 460) | sumAndCount() {
method take (line 480) | take(nr) {
method toArray (line 516) | toArray() {
method toList (line 548) | toList() {
method where (line 561) | where(condition) {
method [Symbol.iterator] (line 59) | [Symbol.iterator]() {
function _ensureIterable (line 580) | function _ensureIterable(src) {
function _ensureFunction (line 591) | function _ensureFunction(f) {
function _toNumber (line 598) | function _toNumber(obj) {
function _toArray (line 604) | function _toArray(iterable) {
function _ensureInternalCount (line 613) | function _ensureInternalCount(enumerable) {
function _ensureInternalTryGetAt (line 644) | function _ensureInternalTryGetAt(enumerable) {
FILE: qUnit/qunit-2.9.2.js
function consoleProxy (line 42) | function consoleProxy(method) {
function defineProperties (line 77) | function defineProperties(target, props) {
function detectPerformanceApi (line 154) | function detectPerformanceApi() {
function measure (line 158) | function measure(comment, startMark, endMark) {
function diff (line 175) | function diff(a, b) {
function inArray (line 200) | function inArray(elem, array) {
function objectValues (line 211) | function objectValues(obj) {
function extend (line 224) | function extend(a, b, undefOnly) {
function objectType (line 238) | function objectType(obj) {
function is (line 273) | function is(type, obj) {
function generateHash (line 279) | function generateHash(module, testName) {
function useStrictEquality (line 311) | function useStrictEquality(a, b) {
function compareConstructors (line 328) | function compareConstructors(a, b) {
function getRegExpFlags (line 356) | function getRegExpFlags(regexp) {
function isContainer (line 360) | function isContainer(val) {
function breadthFirstCompareChild (line 364) | function breadthFirstCompareChild(a, b) {
function typeEquiv (line 582) | function typeEquiv(a, b) {
function innerEquiv (line 596) | function innerEquiv(a, b) {
function quote (line 709) | function quote(str) {
function literal (line 712) | function literal(o) {
function join (line 715) | function join(pre, arr, post) {
function array (line 727) | function array(arr, stack) {
function isArray (line 743) | function isArray(obj) {
function SuiteReport (line 991) | function SuiteReport(name, parentSuite) {
function createModule (line 1123) | function createModule(name, testEnvironment, modifiers) {
function processModule (line 1161) | function processModule(name, options, executeNow) {
function module$1 (line 1211) | function module$1(name, options, executeNow) {
function emit (line 1263) | function emit(eventName, data) {
function on (line 1286) | function on(eventName, callback) {
function objectOrFunction (line 1306) | function objectOrFunction(x) {
function isFunction (line 1311) | function isFunction(x) {
function setScheduler (line 1348) | function setScheduler(scheduleFn) {
function setAsap (line 1352) | function setAsap(asapFn) {
function useNextTick (line 1365) | function useNextTick() {
function useVertxTimer (line 1374) | function useVertxTimer() {
function useMutationObserver (line 1384) | function useMutationObserver() {
function useMessageChannel (line 1396) | function useMessageChannel() {
function useSetTimeout (line 1404) | function useSetTimeout() {
function flush (line 1414) | function flush() {
function attemptVertx (line 1428) | function attemptVertx() {
function then (line 1452) | function then(onFulfillment, onRejection) {
function resolve$1 (line 1507) | function resolve$1(object) {
function noop (line 1522) | function noop() {}
function selfFulfillment (line 1530) | function selfFulfillment() {
function cannotReturnOwn (line 1534) | function cannotReturnOwn() {
function getThen (line 1538) | function getThen(promise) {
function tryThen (line 1547) | function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
function handleForeignThenable (line 1555) | function handleForeignThenable(promise, thenable, then$$1) {
function handleOwnThenable (line 1584) | function handleOwnThenable(promise, thenable) {
function handleMaybeThenable (line 1598) | function handleMaybeThenable(promise, maybeThenable, then$$1) {
function resolve (line 1615) | function resolve(promise, value) {
function publishRejection (line 1625) | function publishRejection(promise) {
function fulfill (line 1633) | function fulfill(promise, value) {
function reject (line 1646) | function reject(promise, reason) {
function subscribe (line 1656) | function subscribe(parent, child, onFulfillment, onRejection) {
function publish (line 1672) | function publish(promise) {
function tryCatch (line 1698) | function tryCatch(callback, detail) {
function invokeCallback (line 1707) | function invokeCallback(settled, promise, callback, detail) {
function initializePromise (line 1747) | function initializePromise(promise, resolver) {
function nextId (line 1760) | function nextId() {
function makePromise (line 1764) | function makePromise(promise) {
function validationError (line 1771) | function validationError() {
function Enumerator (line 1776) | function Enumerator(Constructor, input) {
function all (line 1923) | function all(entries) {
function race (line 1992) | function race(entries) {
function reject$1 (line 2044) | function reject$1(reason) {
function needsResolver (line 2052) | function needsResolver() {
function needsNew (line 2056) | function needsNew() {
function Promise (line 2165) | function Promise(resolver) {
function polyfill (line 2443) | function polyfill() {
function registerLoggingCallbacks (line 2483) | function registerLoggingCallbacks(obj) {
function runLoggingCallbacks (line 2513) | function runLoggingCallbacks(key, args) {
function extractStacktrace (line 2539) | function extractStacktrace(e, offset) {
function sourceFromStacktrace (line 2565) | function sourceFromStacktrace(offset) {
function advance (line 2593) | function advance() {
function advanceTaskQueue (line 2604) | function advanceTaskQueue() {
function processTaskQueue (line 2617) | function processTaskQueue(start) {
function advanceTestQueue (line 2639) | function advanceTestQueue() {
function addToTaskQueue (line 2659) | function addToTaskQueue(tasksArray) {
function taskQueueLength (line 2667) | function taskQueueLength() {
function addToTestQueue (line 2677) | function addToTestQueue(testTasksFunc, prioritize, seed) {
function unitSamplerGenerator (line 2696) | function unitSamplerGenerator(seed) {
function done (line 2719) | function done() {
function TestReport (line 2777) | function TestReport(name, suite, options) {
function Test (line 2892) | function Test(settings) {
function getNotStartedModules (line 2964) | function getNotStartedModules(startModule) {
function runTest (line 3043) | function runTest(test) {
function processHooks (line 3102) | function processHooks(test, module) {
function logSuiteEnd (line 3219) | function logSuiteEnd(module) {
function runTest (line 3251) | function runTest() {
function moduleChainNameMatch (line 3392) | function moduleChainNameMatch(testModule) {
function moduleChainIdMatch (line 3403) | function moduleChainIdMatch(testModule) {
function pushFailure (line 3459) | function pushFailure() {
function saveGlobal (line 3470) | function saveGlobal() {
function checkPollution (line 3487) | function checkPollution() {
function test (line 3506) | function test(testName, callback) {
function todo (line 3519) | function todo(testName, callback) {
function skip (line 3534) | function skip(testName) {
function only (line 3548) | function only(testName, callback) {
function internalStop (line 3565) | function internalStop(test) {
function internalRecover (line 3602) | function internalRecover(test) {
function internalStart (line 3608) | function internalStart(test) {
function collectTests (line 3652) | function collectTests(module) {
function numberOfTests (line 3666) | function numberOfTests(module) {
function numberOfUnskippedTests (line 3670) | function numberOfUnskippedTests(module) {
function notifyTestsRan (line 3676) | function notifyTestsRan(module, skipped) {
function Assert (line 3690) | function Assert(testContext) {
function errorString (line 4141) | function errorString(error) {
function exportQUnit (line 4165) | function exportQUnit(QUnit) {
function onError (line 4207) | function onError(error) {
function onUnhandledRejection (line 4227) | function onUnhandledRejection(reason) {
function scheduleBegin (line 4354) | function scheduleBegin() {
function unblockAndAdvanceQueue (line 4368) | function unblockAndAdvanceQueue() {
function begin (line 4373) | function begin() {
function storeFixture (line 4420) | function storeFixture() {
function resetFixture (line 4436) | function resetFixture() {
function getUrlParams (line 4524) | function getUrlParams() {
function decodeQueryParam (line 4548) | function decodeQueryParam(param) {
function escapeText (line 4561) | function escapeText(s) {
function addEvent (line 4600) | function addEvent(elem, type, fn) {
function removeEvent (line 4604) | function removeEvent(elem, type, fn) {
function addEvents (line 4608) | function addEvents(elems, type, fn) {
function hasClass (line 4615) | function hasClass(elem, name) {
function addClass (line 4619) | function addClass(elem, name) {
function toggleClass (line 4625) | function toggleClass(elem, name, force) {
function removeClass (line 4633) | function removeClass(elem, name) {
function id (line 4645) | function id(name) {
function abortTests (line 4649) | function abortTests() {
function interceptNavigation (line 4659) | function interceptNavigation(ev) {
function getUrlConfigHtml (line 4669) | function getUrlConfigHtml() {
function toolbarChanged (line 4723) | function toolbarChanged() {
function setUrl (line 4794) | function setUrl(params) {
function applyUrlParams (line 4823) | function applyUrlParams() {
function toolbarUrlConfigContainer (line 4845) | function toolbarUrlConfigContainer() {
function abortTestsButton (line 4857) | function abortTestsButton() {
function toolbarLooseFilter (line 4865) | function toolbarLooseFilter() {
function moduleListHtml (line 4892) | function moduleListHtml() {
function toolbarModuleFilter (line 4907) | function toolbarModuleFilter() {
function appendToolbar (line 5059) | function appendToolbar() {
function appendHeader (line 5070) | function appendHeader() {
function appendBanner (line 5078) | function appendBanner() {
function appendTestResults (line 5086) | function appendTestResults() {
function appendFilteredTest (line 5110) | function appendFilteredTest() {
function appendUserAgent (line 5118) | function appendUserAgent() {
function appendInterface (line 5127) | function appendInterface() {
function appendTest (line 5141) | function appendTest(name, testId, moduleName) {
function getNameHtml (line 5244) | function getNameHtml(name, module) {
function stripHtml (line 5272) | function stripHtml(string) {
function DiffMatchPatch (line 5562) | function DiffMatchPatch() {}
function diffHalfMatchI (line 5930) | function diffHalfMatchI(longtext, shorttext, i) {
function diffLinesToCharsMunge (line 6411) | function diffLinesToCharsMunge(text) {
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (622K chars).
[
{
"path": ".gitignore",
"chars": 13,
"preview": "node_modules\n"
},
{
"path": ".vscode/settings.json",
"chars": 121,
"preview": "{\n \"es6-css-minify.hideButton\": \"auto\",\n \"es6-css-minify.js.mangle\": true,\n \"es6-css-minify.js.compress\": true\n"
},
{
"path": "LICENSE",
"chars": 1065,
"preview": "MIT License\n\nCopyright (c) 2020 Siderite\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
},
{
"path": "LInQer.Enumerable.ts",
"chars": 22358,
"preview": "/// <reference path=\"./LInQer.Slim.ts\" />\n\nnamespace Linqer {\n\n\texport interface Enumerable extends Iterable<any> {\n\t\t/*"
},
{
"path": "LInQer.GroupEnumerable.ts",
"chars": 5493,
"preview": "/// <reference path=\"./LInQer.Slim.ts\" />\n\nnamespace Linqer {\n\n\texport interface Enumerable extends Iterable<any> {\n\t\t/*"
},
{
"path": "LInQer.OrderedEnumerable.ts",
"chars": 12816,
"preview": "/// <reference path=\"./LInQer.Slim.ts\" />\nnamespace Linqer {\n\n\texport interface Enumerable extends Iterable<any> {\n\t\t/**"
},
{
"path": "LInQer.Slim.ts",
"chars": 21843,
"preview": "namespace Linqer {\n\n\t/**\n\t * wrapper class over iterable instances that exposes the methods usually found in .NET LINQ\n\t"
},
{
"path": "LInQer.all.d.ts",
"chars": 28908,
"preview": "declare namespace Linqer {\n /**\n * wrapper class over iterable instances that exposes the methods usually found i"
},
{
"path": "LInQer.all.js",
"chars": 78100,
"preview": "\"use strict\";\nvar Linqer;\n(function (Linqer) {\n /**\n * wrapper class over iterable instances that exposes the met"
},
{
"path": "LInQer.extra.js",
"chars": 14125,
"preview": "\"use strict\";\n/// <reference path=\"./LInQer.Slim.ts\" />\n/// <reference path=\"./LInQer.Enumerable.ts\" />\n/// <reference p"
},
{
"path": "LInQer.extra.ts",
"chars": 17780,
"preview": "/// <reference path=\"./LInQer.Slim.ts\" />\n/// <reference path=\"./LInQer.Enumerable.ts\" />\n/// <reference path=\"./LInQer."
},
{
"path": "LInQer.js",
"chars": 64025,
"preview": "\"use strict\";\nvar Linqer;\n(function (Linqer) {\n /**\n * wrapper class over iterable instances that exposes the met"
},
{
"path": "LInQer.slim.js",
"chars": 25955,
"preview": "\"use strict\";\nvar Linqer;\n(function (Linqer) {\n /**\n * wrapper class over iterable instances that exposes the met"
},
{
"path": "README.md",
"chars": 4869,
"preview": "MAJOR UPDATE: I've rewritten the entire thing into [linqer-ts](https://www.npmjs.com/package/@siderite/linqer-ts). Any i"
},
{
"path": "Release.txt",
"chars": 2102,
"preview": "2020-03-08 - 1.2.2\n\t- made typings work for Visual Studio Code for Node.js\n2020-02-16 - 1.2.1\n\t- added comments to the c"
},
{
"path": "build.bat",
"chars": 1040,
"preview": "@ECHO OFF\nREM Delete Javascript files as we will generate them now\nDEL LInQer.extra.js\nDEL LInQer.extra.min.js\nDEL LInQe"
},
{
"path": "npm.export.ts",
"chars": 84,
"preview": "// export to NPM\nif (typeof(module) !== 'undefined') {\n\tmodule.exports = Linqer;\n}\n\n"
},
{
"path": "package.json",
"chars": 769,
"preview": "{\n \"name\": \"@siderite/linqer\",\n \"version\": \"1.3.0\",\n \"description\": \"The C# Language Integrated Queries ported for Ja"
},
{
"path": "qUnit/qunit-2.9.2.css",
"chars": 7875,
"preview": "/*!\n * QUnit 2.9.2\n * https://qunitjs.com/\n *\n * Copyright jQuery Foundation and other contributors\n * Released under th"
},
{
"path": "qUnit/qunit-2.9.2.js",
"chars": 188828,
"preview": "/*!\n * QUnit 2.9.2\n * https://qunitjs.com/\n *\n * Copyright jQuery Foundation and other contributors\n * Released under th"
},
{
"path": "tests.html",
"chars": 451,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width\">\n <title>Un"
},
{
"path": "tests.js",
"chars": 43208,
"preview": "/* eslint-disable no-undef */\nEnumerable = Linqer.Enumerable;\n\n// object and method tests\nQUnit.module('object and metho"
},
{
"path": "tests.performance.html",
"chars": 470,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width\">\n <title>Pe"
},
{
"path": "tests.performance.js",
"chars": 7065,
"preview": "Enumerable = Linqer.Enumerable;\nconst largeNumber = 10000000;\n\n// performance tests\nQUnit.module('performance tests');\n\n"
},
{
"path": "tests.slim.html",
"chars": 426,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width\">\n <title>Un"
},
{
"path": "tests.slim.js",
"chars": 10141,
"preview": "Enumerable = Linqer.Enumerable;\n\n// object and method tests\nQUnit.module('object and method tests');\n\nQUnit.test(\"Enumer"
},
{
"path": "tsconfig.all.json",
"chars": 6042,
"preview": "{\n \"compilerOptions\": {\n /* Basic Options */\n // \"incremental\": true, /* Enable incremental com"
},
{
"path": "tsconfig.extra.json",
"chars": 6043,
"preview": "{\n \"compilerOptions\": {\n /* Basic Options */\n // \"incremental\": true, /* Enable incremental com"
},
{
"path": "tsconfig.json",
"chars": 6024,
"preview": "{\n \"compilerOptions\": {\n /* Basic Options */\n // \"incremental\": true, /* Enable incremental com"
},
{
"path": "tsconfig.slim.json",
"chars": 5931,
"preview": "{\n \"compilerOptions\": {\n /* Basic Options */\n // \"incremental\": true, /* Enable incremental com"
}
]
About this extraction
This page contains the full source code of the Siderite/LInQer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (570.3 KB), approximately 141.7k tokens, and a symbol index with 379 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.