├── .gitignore ├── LICENSE ├── README.md ├── linq.d.ts ├── linq.js ├── package.json ├── sample └── tutorial.js └── test ├── action.js ├── aggregate.js ├── arrayEnumerable.js ├── convert.js ├── dictionary.js ├── enumerable.js ├── errorHandling.js ├── functional.js ├── grouping.js ├── iterator.js ├── join.js ├── ordering.js ├── paging.js ├── projection.js ├── set.js ├── testrunner.js ├── testutils.js └── whereSelectEnumerable.js /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .vs 3 | node_modules 4 | bin 5 | obj 6 | linq.min.js 7 | release/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012 Yoshifumi Kawai, Mihai Ciuraru 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linq 2 | 3 | This is a JavaScript implementation of the .NET [LINQ](https://msdn.microsoft.com/en-us/library/bb308959.aspx) library. 4 | 5 | It contains all the original .NET methods plus a few additions. 6 | 7 | Written in pure JavaScript with no dependencies. 8 | 9 | ## Examples 10 | 11 | ```js 12 | // C# LINQ - delegate 13 | Enumerable.Range(1, 10) 14 | .Where(delegate(int i) { return i % 3 == 0; }) 15 | .Select(delegate(int i) { return i * 10; }); 16 | 17 | // linq.js - anonymous function 18 | Enumerable.range(1, 10) 19 | .where(function(i) { return i % 3 == 0; }) 20 | .select(function(i) { return i * 10; }); 21 | ``` 22 | 23 | ```js 24 | // C# LINQ - lambda 25 | Enumerable.Range(1, 10).Where((i) => i % 3 == 0).Select((i) => i * 10); 26 | 27 | // linq.js - arrow function 28 | Enumerable.range(1, 10).where((i) => i % 3 == 0).select((i) => i * 10); 29 | ``` 30 | 31 | ```js 32 | // C# LINQ - anonymous type 33 | array.Select((val, i) => new { Value: val, Index: i }()); 34 | 35 | // linq.js - object literal 36 | Enumerable.from(array).select((val, i) => ({ value: val, index: i })); 37 | ``` 38 | 39 | See [sample/tutorial.js](https://github.com/mihaifm/linq/blob/master/sample/tutorial.js) and the [test](https://github.com/mihaifm/linq/tree/master/test) folder for more examples. 40 | 41 | # Usage 42 | 43 | ## Node.js (ES modules) 44 | 45 | Install the latest version of the library with npm: 46 | 47 | npm install linq 48 | 49 | Load it in your code with the `import` syntax: 50 | 51 | ```js 52 | import Enumerable from 'linq' 53 | 54 | let result = Enumerable.range(1, 10).where(i => i % 3 == 0).select(i => i * 10) 55 | console.log(result.toArray()) // [ 30, 60, 90 ] 56 | ``` 57 | 58 | Because the library is an ES module, this code will only work if your project is also configured as an ES module. Add the following line in your `package.json` to make it an ES module: 59 | 60 | ```json 61 | "type": "module" 62 | ``` 63 | 64 | If you're not planning to use ES modules, check the CommonJS section below. 65 | 66 | ## Node.js (CommonJS modules) 67 | 68 | Install version 3 of this library: 69 | 70 | npm install linq@3 71 | 72 | Load it with the `require` syntax: 73 | 74 | ```js 75 | const Enumerable = require('linq') 76 | 77 | let count = Enumerable.range(1, 10).count(i => i < 5) 78 | console.log(count) // 4 79 | ``` 80 | 81 | The [cjs](https://github.com/mihaifm/linq/tree/cjs) branch contains the source code for the CommonJS version of the library. 82 | 83 | ## TypeScript 84 | 85 | Install the latest version of the library with npm. 86 | 87 | Configure your compiler options in `tsconfig.json` 88 | 89 | ```json 90 | "compilerOptions": { 91 | "target": "ES2020", 92 | "moduleResolution": "node" 93 | } 94 | ``` 95 | 96 | The library comes with a `d.ts` file containing type definitions for all the objects and methods, feel free to use them in your code: 97 | 98 | ```ts 99 | import Enumerable from 'linq'; 100 | 101 | type tnum = Enumerable.IEnumerable; 102 | let x: tnum = Enumerable.from([1, 2, 3]); 103 | ``` 104 | 105 | ## Deno 106 | 107 | Import the library from deno.land. Use the `@deno-types` annotation to load type definitions: 108 | 109 | ```ts 110 | // @deno-types="https://deno.land/x/linq@4.0.0/linq.d.ts" 111 | import Enumerable from 'https://deno.land/x/linq@4.0.0/linq.js' 112 | 113 | let radius = Enumerable.toInfinity(1).where(r => r * r * Math.PI > 10000).first() 114 | ``` 115 | 116 | You can also install locally with npm. Use the full file path when importing the library: 117 | 118 | ```ts 119 | // @deno-types="./node_modules/linq/linq.d.ts" 120 | import Enumerable from './node_modules/linq/linq.js' 121 | ``` 122 | 123 | ## Browser 124 | 125 | The minified version of the library is available in the [release](https://github.com/mihaifm/linq/releases/latest) archive. 126 | 127 | Load it via ` 131 | 135 | ``` 136 | 137 | You can also load the library via a CDN: 138 | 139 | | CDN | URL | 140 | | ---------: | :----------------------------------------- | 141 | | unpkg | | 142 | | jsDelivr | | 143 | | packd | | 144 | 145 | # Credits 146 | 147 | [Yoshifumi Kawai](https://github.com/neuecc) developed the [original version](https://github.com/neuecc/linq.js/) of this library. 148 | 149 | # License 150 | 151 | [MIT License](https://github.com/mihaifm/linq/blob/master/LICENSE) 152 | -------------------------------------------------------------------------------- /linq.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Enumerable { 2 | export let Utils: { 3 | createLambda(expression: null): (x: unknown) => unknown; 4 | createLambda(expression: string): ((...params: unknown[]) => unknown); 5 | createLambda(expression?: T): T; 6 | createEnumerable(getEnumerator: () => IEnumerator): IEnumerable; 7 | createEnumerator(initialize: () => void, tryGetNext: () => boolean, dispose: () => void): IEnumerator; 8 | extendTo(type: unknown): void; 9 | recallFrom(type: unknown): void; 10 | hasNativeIteratorSupport(): boolean; 11 | } 12 | 13 | export function choice(...params: T[]): IEnumerable; 14 | export function cycle(...params: T[]): IEnumerable; 15 | export function empty(): IEnumerable; 16 | export function from(): IEnumerable; 17 | export function from(obj: IEnumerable): IEnumerable; 18 | export function from(obj: number): IEnumerable; 19 | export function from(obj: boolean): IEnumerable; 20 | export function from(obj: string): IEnumerable; 21 | export function from(obj: T[]): IEnumerable; 22 | export function from(obj: Iterator): IEnumerable; 23 | export function from(obj: Iterable): IEnumerable; 24 | export function from(obj: { length: number;[x: number]: T; }): IEnumerable; 25 | export function from(obj: Record): IEnumerable<{ key: K; value: T }>; 26 | export function make(element: T): IEnumerable; 27 | export function matches(input: string, pattern: RegExp): IEnumerable; 28 | export function matches(input: string, pattern: string, flags?: string): IEnumerable; 29 | export function range(start: number, count: number, step?: number): IEnumerable; 30 | export function rangeDown(start: number, count: number, step?: number): IEnumerable; 31 | export function rangeTo(start: number, to: number, step?: number): IEnumerable; 32 | export function repeat(element: T, count?: number): IEnumerable; 33 | export function repeatWithFinalize(initializer: () => T, finalizer: (element: T) => void): IEnumerable; 34 | export function generate(func: () => T, count?: number): IEnumerable; 35 | export function toInfinity(start?: number, step?: number): IEnumerable; 36 | export function toNegativeInfinity(start?: number, step?: number): IEnumerable; 37 | export function unfold(seed: T, func: (value: T) => T): IEnumerable; 38 | export function defer(enumerableFactory: () => IEnumerable): IEnumerable; 39 | 40 | export interface IEnumerable { 41 | (getEnumerator: () => IEnumerator): void; 42 | getEnumerator(): IEnumerator; 43 | [Symbol.iterator](): Iterator; 44 | 45 | // Extension Methods 46 | traverseBreadthFirst(childrenSelector: (element: T) => IEnumerable): IEnumerable; 47 | traverseBreadthFirst(childrenSelector: (element: T) => IEnumerable, resultSelector: (element: T, nestLevel: number) => TResult): IEnumerable; 48 | traverseDepthFirst(childrenSelector: (element: T) => IEnumerable, resultSelector?: (element: T, nestLevel: number) => TResult): IEnumerable; 49 | flatten(): IEnumerable; 50 | pairwise(selector: (prev: T, current: T) => TResult): IEnumerable; 51 | scan(func: (prev: T, current: T) => T): IEnumerable; 52 | scan(seed: TAccumulate, func: (prev: TAccumulate, current: T) => TAccumulate): IEnumerable; 53 | select(selector: (element: T, index: number) => TResult): IEnumerable; 54 | selectMany(collectionSelector: (element: T, index: number) => IEnumerable): IEnumerable; 55 | selectMany(collectionSelector: (element: T, index: number) => IEnumerable, resultSelector: (outer: T, inner: TCollection) => TResult): IEnumerable; 56 | selectMany(collectionSelector: (element: T, index: number) => TOther[]): IEnumerable; 57 | selectMany(collectionSelector: (element: T, index: number) => TCollection[], resultSelector: (outer: T, inner: TCollection) => TResult): IEnumerable; 58 | selectMany(collectionSelector: (element: T, index: number) => { length: number;[x: number]: TOther; }): IEnumerable; 59 | selectMany(collectionSelector: (element: T, index: number) => { length: number;[x: number]: TCollection; }, resultSelector: (outer: T, inner: TCollection) => TResult): IEnumerable; 60 | where(predicate: (element: T, index: number) => element is TOther): IEnumerable; 61 | where(predicate: (element: T, index: number) => boolean): IEnumerable; 62 | choose(selector: (element: T, index: number) => T): IEnumerable; 63 | ofType(type: unknown): IEnumerable; 64 | zip(second: IEnumerable, resultSelector: (first: T, second: U, index: number) => TResult): IEnumerable; 65 | zip(second: { length: number;[x: number]: U; }, resultSelector: (first: T, second: U, index: number) => TResult): IEnumerable; 66 | zip(second: U[], resultSelector: (first: T, second: U, index: number) => TResult): IEnumerable; 67 | zip(...params: unknown[]): IEnumerable; // last one is selector 68 | merge(...params: (T[] | IEnumerable | { length: number;[x: number]: T; })[]): IEnumerable; 69 | join(inner: IEnumerable, outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: TInner) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 70 | join(inner: { length: number;[x: number]: TInner; }, outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: TInner) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 71 | join(inner: TInner[], outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: TInner) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 72 | leftJoin(inner: IEnumerable, outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: TInner) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 73 | leftJoin(inner: { length: number;[x: number]: TInner; }, outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: TInner) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 74 | leftJoin(inner: TInner[], outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: TInner) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 75 | groupJoin(inner: IEnumerable, outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: IEnumerable) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 76 | groupJoin(inner: { length: number;[x: number]: TInner; }, outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: IEnumerable) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 77 | groupJoin(inner: TInner[], outerKeySelector: (outer: T) => TKey, innerKeySelector: (inner: TInner) => TKey, resultSelector: (outer: T, inner: IEnumerable) => TResult, compareSelector?: (obj: T) => TKey): IEnumerable; 78 | all(predicate: (element: T) => boolean): boolean; 79 | any(predicate?: (element: T) => boolean): boolean; 80 | isEmpty(): boolean; 81 | concat(...sequences: (T[] | IEnumerable | { length: number;[x: number]: T; })[]): IEnumerable; 82 | insert(index: number, second: IEnumerable): IEnumerable; 83 | insert(index: number, second: { length: number;[x: number]: T; }): IEnumerable; 84 | alternate(alternateValue: T): IEnumerable; 85 | alternate(alternateSequence: { length: number;[x: number]: T; }): IEnumerable; 86 | alternate(alternateSequence: IEnumerable): IEnumerable; 87 | alternate(alternateSequence: T[]): IEnumerable; 88 | contains(value: T): boolean; 89 | contains(value: T, compareSelector?: (element: T) => TCompare): boolean; 90 | defaultIfEmpty(defaultValue?: T): IEnumerable; 91 | distinct(): IEnumerable; 92 | distinct(compareSelector: (element: T) => TCompare): IEnumerable; 93 | distinctUntilChanged(): IEnumerable; 94 | distinctUntilChanged(compareSelector: (element: T) => TCompare): IEnumerable; 95 | except(second: { length: number;[x: number]: T; }): IEnumerable; 96 | except(second: { length: number;[x: number]: T; }, compareSelector: (element: T) => TCompare): IEnumerable; 97 | except(second: IEnumerable): IEnumerable; 98 | except(second: IEnumerable, compareSelector: (element: T) => TCompare): IEnumerable; 99 | except(second: T[]): IEnumerable; 100 | except(second: T[], compareSelector: (element: T) => TCompare): IEnumerable; 101 | intersect(second: { length: number;[x: number]: T; }): IEnumerable; 102 | intersect(second: { length: number;[x: number]: T; }, compareSelector: (element: T) => TCompare): IEnumerable; 103 | intersect(second: IEnumerable): IEnumerable; 104 | intersect(second: IEnumerable, compareSelector: (element: T) => TCompare): IEnumerable; 105 | intersect(second: T[]): IEnumerable; 106 | intersect(second: T[], compareSelector: (element: T) => TCompare): IEnumerable; 107 | union(second: { length: number;[x: number]: T; }): IEnumerable; 108 | union(second: { length: number;[x: number]: T; }, compareSelector: (element: T) => TCompare): IEnumerable; 109 | union(second: IEnumerable): IEnumerable; 110 | union(second: IEnumerable, compareSelector: (element: T) => TCompare): IEnumerable; 111 | union(second: T[]): IEnumerable; 112 | union(second: T[], compareSelector: (element: T) => TCompare): IEnumerable; 113 | sequenceEqual(second: { length: number;[x: number]: T; }): boolean; 114 | sequenceEqual(second: { length: number;[x: number]: T; }, compareSelector: (element: T) => TCompare): boolean; 115 | sequenceEqual(second: IEnumerable): boolean; 116 | sequenceEqual(second: IEnumerable, compareSelector: (element: T) => TCompare): boolean; 117 | sequenceEqual(second: T[]): boolean; 118 | sequenceEqual(second: T[], compareSelector: (element: T) => TCompare): boolean; 119 | orderBy(keySelector: (element: T) => TKey): IOrderedEnumerable; 120 | orderBy(keySelector: (element: T) => TKey, comparer: (first: TKey, second: TKey) => number): IOrderedEnumerable; 121 | orderByDescending(keySelector: (element: T) => TKey): IOrderedEnumerable; 122 | orderByDescending(keySelector: (element: T) => TKey, comparer: (first: TKey, second: TKey) => number): IOrderedEnumerable; 123 | reverse(): IEnumerable; 124 | shuffle(): IEnumerable; 125 | weightedSample(weightSelector: (element: T) => number): IEnumerable; 126 | groupBy(keySelector: (element: T) => TKey): IEnumerable>; 127 | groupBy(keySelector: (element: T) => TKey, elementSelector: (element: T) => TElement): IEnumerable>; 128 | groupBy(keySelector: (element: T) => TKey, elementSelector: (element: T) => TElement, resultSelector: (key: TKey, element: IEnumerable) => TResult): IEnumerable; 129 | groupBy(keySelector: (element: T) => TKey, elementSelector: (element: T) => TElement, resultSelector: (key: TKey, element: IEnumerable) => TResult, compareSelector: (element: TKey) => TCompare): IEnumerable; 130 | partitionBy(keySelector: (element: T) => TKey): IEnumerable>; 131 | partitionBy(keySelector: (element: T) => TKey, elementSelector: (element: T) => TElement): IEnumerable>; 132 | partitionBy(keySelector: (element: T) => TKey, elementSelector: (element: T) => TElement, resultSelector: (key: TKey, element: IEnumerable) => TResult): IEnumerable; 133 | partitionBy(keySelector: (element: T) => TKey, elementSelector: (element: T) => TElement, resultSelector: (key: TKey, element: IEnumerable) => TResult, compareSelector: (element: TKey) => TCompare): IEnumerable; 134 | buffer(count: number): IEnumerable; 135 | aggregate(func: (prev: T, current: T) => T): T; 136 | aggregate(seed: TAccumulate, func: (prev: TAccumulate, current: T) => TAccumulate): TAccumulate; 137 | aggregate(seed: TAccumulate, func: (prev: TAccumulate, current: T) => TAccumulate, resultSelector: (last: TAccumulate) => TResult): TResult; 138 | average(selector?: (element: T) => number): number; 139 | count(predicate?: (element: T, index: number) => boolean): number; 140 | max(selector?: (element: T) => number): number; 141 | min(selector?: (element: T) => number): number; 142 | maxBy(keySelector: (element: T) => TKey): T; 143 | minBy(keySelector: (element: T) => TKey): T; 144 | sum(selector?: (element: T) => number): number; 145 | elementAt(index: number): T; 146 | elementAtOrDefault(index: number, defaultValue?: T): T | undefined; 147 | first(predicate: (element: T, index: number) => element is TOther): TOther; 148 | first(predicate?: (element: T, index: number) => boolean): T; 149 | firstOrDefault(predicate: (element: T, index: number) => element is TOther, defaultValue: TDefault): TOther | TDefault; 150 | firstOrDefault(predicate: (element: T, index: number) => boolean, defaultValue: TDefault): T | TDefault; 151 | firstOrDefault(predicate: (element: T, index: number) => element is TOther): TOther | undefined; 152 | firstOrDefault(predicate: (element: T, index: number) => boolean): T | undefined; 153 | firstOrDefault(defaultValue: TDefault): T | TDefault; 154 | firstOrDefault(): T | undefined; 155 | last(predicate: (element: T, index: number) => element is TOther): TOther; 156 | last(predicate?: (element: T, index: number) => boolean): T; 157 | lastOrDefault(predicate: (element: T, index: number) => element is TOther, defaultValue: TDefault): TOther | TDefault; 158 | lastOrDefault(predicate: (element: T, index: number) => boolean, defaultValue: TDefault): T | TDefault; 159 | lastOrDefault(predicate: (element: T, index: number) => element is TOther): TOther | undefined; 160 | lastOrDefault(predicate: (element: T, index: number) => boolean): T | undefined; 161 | lastOrDefault(defaultValue: TDefault): T | TDefault; 162 | lastOrDefault(): T | undefined; 163 | single(predicate: (element: T, index: number) => element is TOther): TOther; 164 | single(predicate?: (element: T, index: number) => boolean): T; 165 | singleOrDefault(predicate: (element: T, index: number) => element is TOther, defaultValue: TDefault): TOther | TDefault; 166 | singleOrDefault(predicate: (element: T, index: number) => boolean, defaultValue: TDefault): T | TDefault; 167 | singleOrDefault(predicate: (element: T, index: number) => element is TOther): TOther | undefined; 168 | singleOrDefault(predicate: (element: T, index: number) => boolean): T | undefined; 169 | singleOrDefault(defaultValue: TDefault): T | TDefault; 170 | singleOrDefault(): T | undefined; 171 | skip(count: number): IEnumerable; 172 | skipWhile(predicate: (element: T, index: number) => boolean): IEnumerable; 173 | take(count: number): IEnumerable; 174 | takeWhile(predicate: (element: T, index: number) => boolean): IEnumerable; 175 | takeExceptLast(count?: number): IEnumerable; 176 | takeFromLast(count: number): IEnumerable; 177 | indexOf(item: T): number; 178 | indexOf(predicate: (element: T, index: number) => boolean): number; 179 | lastIndexOf(item: T): number; 180 | lastIndexOf(predicate: (element: T, index: number) => boolean): number; 181 | asEnumerable(): IEnumerable; 182 | cast(): IEnumerable; 183 | toArray(): T[]; 184 | toLookup(keySelector: (element: T) => TKey): ILookup; 185 | toLookup(keySelector: (element: T) => TKey, elementSelector: (element: T) => TElement): ILookup; 186 | toLookup(keySelector: (element: T) => TKey, elementSelector: (element: T) => TElement, compareSelector: (key: TKey) => TCompare): ILookup; 187 | toObject(keySelector: (element: T) => TKey, elementSelector?: (element: T) => TElement): Record; 188 | toDictionary(keySelector: (element: T) => TKey): IDictionary; 189 | toDictionary(keySelector: (element: T) => TKey, elementSelector: (element: T) => TValue): IDictionary; 190 | toDictionary(keySelector: (element: T) => TKey, elementSelector: (element: T) => TValue, compareSelector: (key: TKey) => TCompare): IDictionary; 191 | toJSONString(replacer?: (key: string, value: unknown) => unknown, space?: string | number): string; 192 | toJSONString(replacer?: (string | number)[], space?: string | number): string; 193 | toJoinedString(separator?: string): string; 194 | toJoinedString(separator: string, selector: (element: T, index: number) => TResult): string; 195 | doAction(action: (element: T, index: number) => void): IEnumerable; 196 | doAction(action: (element: T, index: number) => boolean): IEnumerable; 197 | forEach(action: (element: T, index: number) => void): void; 198 | forEach(action: (element: T, index: number) => boolean): void; 199 | force(): void; 200 | letBind(func: (source: IEnumerable) => { length: number;[x: number]: TResult; }): IEnumerable; 201 | letBind(func: (source: IEnumerable) => TResult[]): IEnumerable; 202 | letBind(func: (source: IEnumerable) => IEnumerable): IEnumerable; 203 | share(): IDisposableEnumerable; 204 | memoize(): IDisposableEnumerable; 205 | catchError(handler: string | ((exception: unknown) => void)): IEnumerable; 206 | finallyAction(finallyAction: () => void): IEnumerable; 207 | log(): IEnumerable; 208 | log(selector: (element: T) => TValue): IEnumerable; 209 | trace(message?: string): IEnumerable; 210 | trace(message: string, selector: (element: T) => TValue): IEnumerable; 211 | } 212 | 213 | export interface IEnumerator { 214 | current(): T; 215 | moveNext(): boolean; 216 | dispose(): void; 217 | } 218 | 219 | export interface IOrderedEnumerable extends IEnumerable { 220 | createOrderedEnumerable(keySelector: (element: T) => TKey, comparer?: (first: TKey, second: TKey) => number, descending?: boolean): IOrderedEnumerable; 221 | thenBy(keySelector: (element: T) => TKey): IOrderedEnumerable; 222 | thenBy(keySelector: (element: T) => TKey, comparer: (first: TKey, second: TKey) => number): IOrderedEnumerable; 223 | thenByDescending(keySelector: (element: T) => TKey): IOrderedEnumerable; 224 | thenByDescending(keySelector: (element: T) => TKey, comparer: (first: TKey, second: TKey) => number): IOrderedEnumerable; 225 | } 226 | 227 | export interface IDisposableEnumerable extends IEnumerable { 228 | dispose(): void; 229 | } 230 | 231 | export interface IDictionary { 232 | add(key: TKey, value: TValue): void; 233 | get(key: TKey): TValue; 234 | set(key: TKey, value: TValue): boolean; 235 | contains(key: TKey): boolean; 236 | clear(): void; 237 | remove(key: TKey): void; 238 | count(): number; 239 | toEnumerable(): IEnumerable<{ key: TKey; value: TValue }>; 240 | } 241 | 242 | export interface ILookup { 243 | count(): number; 244 | get(key: TKey): IEnumerable; 245 | contains(key: TKey): boolean; 246 | toEnumerable(): IEnumerable>; 247 | } 248 | 249 | export interface IGrouping extends IEnumerable { 250 | key(): TKey; 251 | getSource(): TElement[]; 252 | } 253 | } 254 | 255 | export default Enumerable; 256 | -------------------------------------------------------------------------------- /linq.js: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | * linq.js - LINQ for JavaScript 3 | * licensed under MIT License 4 | *------------------------------------------------------------------------*/ 5 | 6 | var Functions = { 7 | Identity: function (x) { return x; }, 8 | True: function () { return true; }, 9 | Blank: function () { } 10 | }; 11 | 12 | var Types = { 13 | Boolean: typeof true, 14 | Number: typeof 0, 15 | String: typeof "", 16 | Object: typeof {}, 17 | Undefined: typeof undefined, 18 | Function: typeof function () { } 19 | }; 20 | 21 | var funcCache = { "": Functions.Identity }; 22 | 23 | var Utils = { 24 | createLambda: function (expression) { 25 | if (expression == null) return Functions.Identity; 26 | if (typeof expression === Types.String) { 27 | // get from cache 28 | let f = funcCache[expression]; 29 | if (f != null) { 30 | return f; 31 | } 32 | 33 | if (expression.indexOf("=>") === -1) { 34 | const regexp = new RegExp("[$]+", "g"); 35 | 36 | let maxLength = 0; 37 | let match; 38 | while ((match = regexp.exec(expression)) != null) { 39 | if (match[0].length > maxLength) { 40 | maxLength = match[0].length; 41 | } 42 | } 43 | 44 | const argArray = []; 45 | for (let i = 1; i <= maxLength; i++) { 46 | let dollar = ""; 47 | for (let j = 0; j < i; j++) { 48 | dollar += "$"; 49 | } 50 | argArray.push(dollar); 51 | } 52 | 53 | const args = argArray.join(","); 54 | 55 | f = new Function(args, "return " + expression); 56 | funcCache[expression] = f; 57 | return f; 58 | } 59 | else { 60 | const expr = expression.match(/^[(\s]*([^()]*?)[)\s]*=>(.*)/); 61 | f = new Function(expr[1], (expr[2].match(/\breturn\b/) ? expr[2] : "return " + expr[2])); 62 | funcCache[expression] = f; 63 | return f; 64 | } 65 | } 66 | return expression; 67 | }, 68 | 69 | defineProperty: function (target, methodName, value) { 70 | Object.defineProperty(target, methodName, { 71 | enumerable: false, 72 | configurable: true, 73 | writable: true, 74 | value: value 75 | }) 76 | }, 77 | 78 | compare: function (a, b) { 79 | return (a === b) ? 0 : (a > b) ? 1 : -1; 80 | }, 81 | 82 | dispose: function (obj) { 83 | if (obj != null) obj.dispose(); 84 | }, 85 | 86 | hasNativeIteratorSupport: function () { 87 | return typeof Symbol !== 'undefined' && typeof Symbol.iterator !== 'undefined'; 88 | } 89 | }; 90 | 91 | var State = { Before: 0, Running: 1, After: 2 }; 92 | 93 | var IEnumerator = function (initialize, tryGetNext, dispose) { 94 | var yielder = new Yielder(); 95 | var state = State.Before; 96 | 97 | this.current = yielder.current; 98 | 99 | this.moveNext = function () { 100 | try { 101 | switch (state) { 102 | case State.Before: 103 | state = State.Running; 104 | initialize(); 105 | // fall through 106 | 107 | case State.Running: 108 | if (tryGetNext.apply(yielder)) { 109 | return true; 110 | } 111 | else { 112 | this.dispose(); 113 | return false; 114 | } 115 | // fall through 116 | 117 | case State.After: 118 | return false; 119 | } 120 | } 121 | catch (e) { 122 | this.dispose(); 123 | throw e; 124 | } 125 | }; 126 | 127 | this.dispose = function () { 128 | if (state != State.Running) return; 129 | 130 | try { 131 | dispose(); 132 | } 133 | finally { 134 | state = State.After; 135 | } 136 | }; 137 | }; 138 | 139 | // tryGetNext yielder 140 | var Yielder = function () { 141 | var current = null; 142 | this.current = function () { return current; }; 143 | this.yieldReturn = function (value) { 144 | current = value; 145 | return true; 146 | }; 147 | this.yieldBreak = function () { 148 | return false; 149 | }; 150 | }; 151 | 152 | // Enumerable constuctor 153 | var Enumerable = function (getEnumerator) { 154 | this.getEnumerator = getEnumerator; 155 | }; 156 | 157 | /////////////////// 158 | // Utility Methods 159 | 160 | Enumerable.Utils = {}; 161 | 162 | Enumerable.Utils.createLambda = function (expression) { 163 | return Utils.createLambda(expression); 164 | }; 165 | 166 | Enumerable.Utils.createEnumerable = function (getEnumerator) { 167 | return new Enumerable(getEnumerator); 168 | }; 169 | 170 | Enumerable.Utils.createEnumerator = function (initialize, tryGetNext, dispose) { 171 | return new IEnumerator(initialize, tryGetNext, dispose); 172 | }; 173 | 174 | Enumerable.Utils.extendTo = function (type) { 175 | var typeProto = type.prototype; 176 | var enumerableProto; 177 | 178 | if (type === Array) { 179 | enumerableProto = ArrayEnumerable.prototype; 180 | Utils.defineProperty(typeProto, "getSource", function () { 181 | return this; 182 | }); 183 | } 184 | else { 185 | enumerableProto = Enumerable.prototype; 186 | Utils.defineProperty(typeProto, "getEnumerator", function () { 187 | return Enumerable.from(this).getEnumerator(); 188 | }); 189 | } 190 | 191 | for (let methodName in enumerableProto) { 192 | const func = enumerableProto[methodName]; 193 | 194 | // already extended 195 | if (typeProto[methodName] == func) continue; 196 | 197 | // already defined(example Array#reverse/join/forEach...) 198 | if (typeProto[methodName] != null) { 199 | methodName = methodName + "ByLinq"; 200 | if (typeProto[methodName] == func) continue; // recheck 201 | } 202 | 203 | if (func instanceof Function) { 204 | Utils.defineProperty(typeProto, methodName, func); 205 | } 206 | } 207 | }; 208 | 209 | Enumerable.Utils.recallFrom = function (type) { 210 | var typeProto = type.prototype; 211 | var enumerableProto; 212 | 213 | if (type === Array) { 214 | enumerableProto = ArrayEnumerable.prototype; 215 | delete typeProto.getSource; 216 | } 217 | else { 218 | enumerableProto = Enumerable.prototype; 219 | delete typeProto.getEnumerator; 220 | } 221 | 222 | for (const methodName in enumerableProto) { 223 | const func = enumerableProto[methodName]; 224 | 225 | if (typeProto[methodName + 'ByLinq']) { 226 | delete typeProto[methodName + 'ByLinq']; 227 | } 228 | else if (typeProto[methodName] == func && func instanceof Function) { 229 | delete typeProto[methodName]; 230 | } 231 | } 232 | }; 233 | 234 | ////////////// 235 | // Generators 236 | 237 | Enumerable.choice = function () { 238 | var args = arguments; 239 | 240 | return new Enumerable(function () { 241 | return new IEnumerator( 242 | function () { 243 | args = (args[0] instanceof Array) ? args[0] 244 | : (args[0].getEnumerator != null) ? args[0].toArray() 245 | : args; 246 | }, 247 | function () { 248 | return this.yieldReturn(args[Math.floor(Math.random() * args.length)]); 249 | }, 250 | Functions.Blank); 251 | }); 252 | }; 253 | 254 | Enumerable.cycle = function () { 255 | var args = arguments; 256 | 257 | return new Enumerable(function () { 258 | var index = 0; 259 | return new IEnumerator( 260 | function () { 261 | args = (args[0] instanceof Array) ? args[0] 262 | : (args[0].getEnumerator != null) ? args[0].toArray() 263 | : args; 264 | }, 265 | function () { 266 | if (index >= args.length) index = 0; 267 | return this.yieldReturn(args[index++]); 268 | }, 269 | Functions.Blank); 270 | }); 271 | }; 272 | 273 | Enumerable.empty = function () { 274 | return new Enumerable(function () { 275 | return new IEnumerator( 276 | Functions.Blank, 277 | function () { return false; }, 278 | Functions.Blank); 279 | }); 280 | }; 281 | 282 | Enumerable.from = function (obj) { 283 | if (obj == null) { 284 | return Enumerable.empty(); 285 | } 286 | if (obj instanceof Enumerable) { 287 | return obj; 288 | } 289 | if (typeof obj == Types.Number || typeof obj == Types.Boolean) { 290 | return Enumerable.repeat(obj, 1); 291 | } 292 | if (typeof obj == Types.String) { 293 | return new Enumerable(function () { 294 | var index = 0; 295 | return new IEnumerator( 296 | Functions.Blank, 297 | function () { 298 | return (index < obj.length) ? this.yieldReturn(obj.charAt(index++)) : false; 299 | }, 300 | Functions.Blank); 301 | }); 302 | } 303 | if (typeof obj == Types.Function && Object.keys(obj).length == 0) { 304 | return new Enumerable(function () { 305 | var orig; 306 | 307 | return new IEnumerator( 308 | function () { 309 | orig = obj()[Symbol.iterator](); 310 | }, 311 | function () { 312 | var next = orig.next(); 313 | return (next.done ? false : (this.yieldReturn(next.value))); 314 | }, 315 | Functions.Blank); 316 | }); 317 | } 318 | 319 | if (typeof obj != Types.Function) { 320 | // array or array-like object 321 | if (typeof obj.length == Types.Number) { 322 | return new ArrayEnumerable(obj); 323 | } 324 | 325 | // iterable object 326 | if (typeof Symbol !== 'undefined' && typeof obj[Symbol.iterator] !== 'undefined') { 327 | let iterator; 328 | return new Enumerable(function () { 329 | return new IEnumerator( 330 | function () { iterator = obj[Symbol.iterator]()}, 331 | function () { 332 | var next = iterator.next(); 333 | return (next.done ? false : (this.yieldReturn(next.value))); 334 | }, 335 | Functions.Blank); 336 | }); 337 | } 338 | 339 | // object conforming to the iterator protocol 340 | if (typeof obj.next == Types.Function) { 341 | return new Enumerable(function () { 342 | return new IEnumerator( 343 | Functions.Blank, 344 | function () { 345 | var next = obj.next(); 346 | return (next.done ? false : (this.yieldReturn(next.value))); 347 | }, 348 | Functions.Blank); 349 | }); 350 | } 351 | } 352 | 353 | // case function/object: create keyValuePair[] 354 | return new Enumerable(function () { 355 | var array = []; 356 | var index = 0; 357 | 358 | return new IEnumerator( 359 | function () { 360 | for (const key in obj) { 361 | const value = obj[key]; 362 | if (!(value instanceof Function) && Object.prototype.hasOwnProperty.call(obj, key)) { 363 | array.push({ key: key, value: value }); 364 | } 365 | } 366 | }, 367 | function () { 368 | return (index < array.length) 369 | ? this.yieldReturn(array[index++]) 370 | : false; 371 | }, 372 | Functions.Blank); 373 | }); 374 | }, 375 | 376 | Enumerable.make = function (element) { 377 | return Enumerable.repeat(element, 1); 378 | }; 379 | 380 | // Overload:function(input, pattern) 381 | // Overload:function(input, pattern, flags) 382 | Enumerable.matches = function (input, pattern, flags) { 383 | if (flags == null) flags = ""; 384 | 385 | if (pattern instanceof RegExp) { 386 | flags += (pattern.ignoreCase) ? "i" : ""; 387 | flags += (pattern.multiline) ? "m" : ""; 388 | pattern = pattern.source; 389 | } 390 | if (flags.indexOf("g") === -1) flags += "g"; 391 | 392 | return new Enumerable(function () { 393 | var regex; 394 | return new IEnumerator( 395 | function () { regex = new RegExp(pattern, flags); }, 396 | function () { 397 | var match = regex.exec(input); 398 | return (match) ? this.yieldReturn(match) : false; 399 | }, 400 | Functions.Blank); 401 | }); 402 | }; 403 | 404 | // Overload:function(start, count) 405 | // Overload:function(start, count, step) 406 | Enumerable.range = function (start, count, step) { 407 | if (step == null) step = 1; 408 | 409 | return new Enumerable(function () { 410 | var value; 411 | var index = 0; 412 | 413 | return new IEnumerator( 414 | function () { value = start - step; }, 415 | function () { 416 | return (index++ < count) 417 | ? this.yieldReturn(value += step) 418 | : this.yieldBreak(); 419 | }, 420 | Functions.Blank); 421 | }); 422 | }; 423 | 424 | // Overload:function(start, count) 425 | // Overload:function(start, count, step) 426 | Enumerable.rangeDown = function (start, count, step) { 427 | if (step == null) step = 1; 428 | 429 | return new Enumerable(function () { 430 | var value; 431 | var index = 0; 432 | 433 | return new IEnumerator( 434 | function () { value = start + step; }, 435 | function () { 436 | return (index++ < count) 437 | ? this.yieldReturn(value -= step) 438 | : this.yieldBreak(); 439 | }, 440 | Functions.Blank); 441 | }); 442 | }; 443 | 444 | // Overload:function(start, to) 445 | // Overload:function(start, to, step) 446 | Enumerable.rangeTo = function (start, to, step) { 447 | if (step == null) step = 1; 448 | 449 | if (start < to) { 450 | return new Enumerable(function () { 451 | var value; 452 | 453 | return new IEnumerator( 454 | function () { value = start - step; }, 455 | function () { 456 | var next = value += step; 457 | return (next <= to) 458 | ? this.yieldReturn(next) 459 | : this.yieldBreak(); 460 | }, 461 | Functions.Blank); 462 | }); 463 | } 464 | else { 465 | return new Enumerable(function () { 466 | var value; 467 | 468 | return new IEnumerator( 469 | function () { value = start + step; }, 470 | function () { 471 | var next = value -= step; 472 | return (next >= to) 473 | ? this.yieldReturn(next) 474 | : this.yieldBreak(); 475 | }, 476 | Functions.Blank); 477 | }); 478 | } 479 | }; 480 | 481 | // Overload:function(element) 482 | // Overload:function(element, count) 483 | Enumerable.repeat = function (element, count) { 484 | if (count != null) 485 | return Enumerable.repeat(element).take(count); 486 | 487 | return new Enumerable(function () { 488 | return new IEnumerator( 489 | Functions.Blank, 490 | function () { return this.yieldReturn(element); }, 491 | Functions.Blank); 492 | }); 493 | }; 494 | 495 | Enumerable.repeatWithFinalize = function (initializer, finalizer) { 496 | initializer = Utils.createLambda(initializer); 497 | finalizer = Utils.createLambda(finalizer); 498 | 499 | return new Enumerable(function () { 500 | var element; 501 | return new IEnumerator( 502 | function () { element = initializer(); }, 503 | function () { return this.yieldReturn(element); }, 504 | function () { 505 | if (element != null) { 506 | finalizer(element); 507 | element = null; 508 | } 509 | }); 510 | }); 511 | }; 512 | 513 | // Overload:function(func) 514 | // Overload:function(func, count) 515 | Enumerable.generate = function (func, count) { 516 | if (count != null) 517 | return Enumerable.generate(func).take(count); 518 | 519 | func = Utils.createLambda(func); 520 | 521 | return new Enumerable(function () { 522 | return new IEnumerator( 523 | Functions.Blank, 524 | function () { return this.yieldReturn(func()); }, 525 | Functions.Blank); 526 | }); 527 | }; 528 | 529 | // Overload:function() 530 | // Overload:function(start) 531 | // Overload:function(start, step) 532 | Enumerable.toInfinity = function (start, step) { 533 | if (start == null) start = 0; 534 | if (step == null) step = 1; 535 | 536 | return new Enumerable(function () { 537 | var value; 538 | return new IEnumerator( 539 | function () { value = start - step; }, 540 | function () { return this.yieldReturn(value += step); }, 541 | Functions.Blank); 542 | }); 543 | }; 544 | 545 | // Overload:function() 546 | // Overload:function(start) 547 | // Overload:function(start, step) 548 | Enumerable.toNegativeInfinity = function (start, step) { 549 | if (start == null) start = 0; 550 | if (step == null) step = 1; 551 | 552 | return new Enumerable(function () { 553 | var value; 554 | return new IEnumerator( 555 | function () { value = start + step; }, 556 | function () { return this.yieldReturn(value -= step); }, 557 | Functions.Blank); 558 | }); 559 | }; 560 | 561 | Enumerable.unfold = function (seed, func) { 562 | func = Utils.createLambda(func); 563 | 564 | return new Enumerable(function () { 565 | var isFirst = true; 566 | var value; 567 | return new IEnumerator( 568 | Functions.Blank, 569 | function () { 570 | if (isFirst) { 571 | isFirst = false; 572 | value = seed; 573 | return this.yieldReturn(value); 574 | } 575 | value = func(value); 576 | return this.yieldReturn(value); 577 | }, 578 | Functions.Blank); 579 | }); 580 | }; 581 | 582 | Enumerable.defer = function (enumerableFactory) { 583 | return new Enumerable(function () { 584 | var enumerator; 585 | 586 | return new IEnumerator( 587 | function () { enumerator = Enumerable.from(enumerableFactory()).getEnumerator(); }, 588 | function () { 589 | return (enumerator.moveNext()) 590 | ? this.yieldReturn(enumerator.current()) 591 | : this.yieldBreak(); 592 | }, 593 | function () { 594 | Utils.dispose(enumerator); 595 | }); 596 | }); 597 | }; 598 | 599 | ///////////////////// 600 | // Extension Methods 601 | 602 | //////////////////////////////////// 603 | // Projection and Filtering Methods 604 | 605 | // Overload:function(func) 606 | // Overload:function(func, resultSelector) 607 | // Overload:function(func, resultSelector) 608 | Enumerable.prototype.traverseBreadthFirst = function (func, resultSelector) { 609 | var source = this; 610 | func = Utils.createLambda(func); 611 | resultSelector = Utils.createLambda(resultSelector); 612 | 613 | return new Enumerable(function () { 614 | var enumerator; 615 | var nestLevel = 0; 616 | var buffer = []; 617 | 618 | return new IEnumerator( 619 | function () { enumerator = source.getEnumerator(); }, 620 | function () { 621 | while (true) { 622 | if (enumerator.moveNext()) { 623 | buffer.push(enumerator.current()); 624 | return this.yieldReturn(resultSelector(enumerator.current(), nestLevel)); 625 | } 626 | 627 | const next = Enumerable.from(buffer).selectMany(function (x) { return func(x); }); 628 | if (!next.any()) { 629 | return false; 630 | } 631 | else { 632 | nestLevel++; 633 | buffer = []; 634 | Utils.dispose(enumerator); 635 | enumerator = next.getEnumerator(); 636 | } 637 | } 638 | }, 639 | function () { Utils.dispose(enumerator); }); 640 | }); 641 | }; 642 | 643 | // Overload:function(func) 644 | // Overload:function(func, resultSelector) 645 | // Overload:function(func, resultSelector) 646 | Enumerable.prototype.traverseDepthFirst = function (func, resultSelector) { 647 | var source = this; 648 | func = Utils.createLambda(func); 649 | resultSelector = Utils.createLambda(resultSelector); 650 | 651 | return new Enumerable(function () { 652 | var enumeratorStack = []; 653 | var enumerator; 654 | 655 | return new IEnumerator( 656 | function () { enumerator = source.getEnumerator(); }, 657 | function () { 658 | while (true) { 659 | if (enumerator.moveNext()) { 660 | const value = resultSelector(enumerator.current(), enumeratorStack.length); 661 | enumeratorStack.push(enumerator); 662 | enumerator = Enumerable.from(func(enumerator.current())).getEnumerator(); 663 | return this.yieldReturn(value); 664 | } 665 | 666 | if (enumeratorStack.length <= 0) return false; 667 | Utils.dispose(enumerator); 668 | enumerator = enumeratorStack.pop(); 669 | } 670 | }, 671 | function () { 672 | try { 673 | Utils.dispose(enumerator); 674 | } 675 | finally { 676 | Enumerable.from(enumeratorStack).forEach(function (s) { s.dispose(); }); 677 | } 678 | }); 679 | }); 680 | }; 681 | 682 | Enumerable.prototype.flatten = function () { 683 | var source = this; 684 | 685 | return new Enumerable(function () { 686 | var enumerator; 687 | var middleEnumerator = null; 688 | 689 | return new IEnumerator( 690 | function () { enumerator = source.getEnumerator(); }, 691 | function () { 692 | while (true) { 693 | if (middleEnumerator != null) { 694 | if (middleEnumerator.moveNext()) { 695 | return this.yieldReturn(middleEnumerator.current()); 696 | } 697 | else { 698 | middleEnumerator = null; 699 | } 700 | } 701 | 702 | if (enumerator.moveNext()) { 703 | if (enumerator.current() instanceof Array) { 704 | Utils.dispose(middleEnumerator); 705 | middleEnumerator = Enumerable.from(enumerator.current()) 706 | .selectMany(Functions.Identity) 707 | .flatten() 708 | .getEnumerator(); 709 | continue; 710 | } 711 | else { 712 | return this.yieldReturn(enumerator.current()); 713 | } 714 | } 715 | 716 | return false; 717 | } 718 | }, 719 | function () { 720 | try { 721 | Utils.dispose(enumerator); 722 | } 723 | finally { 724 | Utils.dispose(middleEnumerator); 725 | } 726 | }); 727 | }); 728 | }; 729 | 730 | Enumerable.prototype.pairwise = function (selector) { 731 | var source = this; 732 | selector = Utils.createLambda(selector); 733 | 734 | return new Enumerable(function () { 735 | var enumerator; 736 | 737 | return new IEnumerator( 738 | function () { 739 | enumerator = source.getEnumerator(); 740 | enumerator.moveNext(); 741 | }, 742 | function () { 743 | var prev = enumerator.current(); 744 | return (enumerator.moveNext()) 745 | ? this.yieldReturn(selector(prev, enumerator.current())) 746 | : false; 747 | }, 748 | function () { Utils.dispose(enumerator); }); 749 | }); 750 | }; 751 | 752 | // Overload:function(func) 753 | // Overload:function(seed,func) 754 | Enumerable.prototype.scan = function (seed, func) { 755 | var isUseSeed; 756 | if (func == null) { 757 | func = Utils.createLambda(seed); 758 | isUseSeed = false; 759 | } else { 760 | func = Utils.createLambda(func); 761 | isUseSeed = true; 762 | } 763 | var source = this; 764 | 765 | return new Enumerable(function () { 766 | var enumerator; 767 | var value; 768 | var isFirst = true; 769 | 770 | return new IEnumerator( 771 | function () { enumerator = source.getEnumerator(); }, 772 | function () { 773 | if (isFirst) { 774 | isFirst = false; 775 | if (!isUseSeed) { 776 | if (enumerator.moveNext()) { 777 | return this.yieldReturn(value = enumerator.current()); 778 | } 779 | } 780 | else { 781 | return this.yieldReturn(value = seed); 782 | } 783 | } 784 | 785 | return (enumerator.moveNext()) 786 | ? this.yieldReturn(value = func(value, enumerator.current())) 787 | : false; 788 | }, 789 | function () { Utils.dispose(enumerator); }); 790 | }); 791 | }; 792 | 793 | // Overload:function(selector) 794 | // Overload:function(selector) 795 | Enumerable.prototype.select = function (selector) { 796 | selector = Utils.createLambda(selector); 797 | 798 | if (selector.length <= 1) { 799 | return new WhereSelectEnumerable(this, null, selector); 800 | } 801 | else { 802 | var source = this; 803 | 804 | return new Enumerable(function () { 805 | var enumerator; 806 | var index = 0; 807 | 808 | return new IEnumerator( 809 | function () { enumerator = source.getEnumerator(); }, 810 | function () { 811 | return (enumerator.moveNext()) 812 | ? this.yieldReturn(selector(enumerator.current(), index++)) 813 | : false; 814 | }, 815 | function () { Utils.dispose(enumerator); }); 816 | }); 817 | } 818 | }; 819 | 820 | // Overload:function(collectionSelector) 821 | // Overload:function(collectionSelector) 822 | // Overload:function(collectionSelector,resultSelector) 823 | // Overload:function(collectionSelector,resultSelector) 824 | Enumerable.prototype.selectMany = function (collectionSelector, resultSelector) { 825 | var source = this; 826 | collectionSelector = Utils.createLambda(collectionSelector); 827 | if (resultSelector == null) resultSelector = function (a, b) { return b; }; 828 | resultSelector = Utils.createLambda(resultSelector); 829 | 830 | return new Enumerable(function () { 831 | var enumerator; 832 | var middleEnumerator = undefined; 833 | var index = 0; 834 | 835 | return new IEnumerator( 836 | function () { enumerator = source.getEnumerator(); }, 837 | function () { 838 | if (middleEnumerator === undefined) { 839 | if (!enumerator.moveNext()) return false; 840 | } 841 | do { 842 | if (middleEnumerator == null) { 843 | const middleSeq = collectionSelector(enumerator.current(), index++); 844 | middleEnumerator = Enumerable.from(middleSeq).getEnumerator(); 845 | } 846 | if (middleEnumerator.moveNext()) { 847 | return this.yieldReturn(resultSelector(enumerator.current(), middleEnumerator.current())); 848 | } 849 | Utils.dispose(middleEnumerator); 850 | middleEnumerator = null; 851 | } while (enumerator.moveNext()); 852 | return false; 853 | }, 854 | function () { 855 | try { 856 | Utils.dispose(enumerator); 857 | } 858 | finally { 859 | Utils.dispose(middleEnumerator); 860 | } 861 | }); 862 | }); 863 | }; 864 | 865 | // Overload:function(predicate) 866 | // Overload:function(predicate) 867 | Enumerable.prototype.where = function (predicate) { 868 | predicate = Utils.createLambda(predicate); 869 | 870 | if (predicate.length <= 1) { 871 | return new WhereEnumerable(this, predicate); 872 | } 873 | else { 874 | var source = this; 875 | 876 | return new Enumerable(function () { 877 | var enumerator; 878 | var index = 0; 879 | 880 | return new IEnumerator( 881 | function () { enumerator = source.getEnumerator(); }, 882 | function () { 883 | while (enumerator.moveNext()) { 884 | if (predicate(enumerator.current(), index++)) { 885 | return this.yieldReturn(enumerator.current()); 886 | } 887 | } 888 | return false; 889 | }, 890 | function () { Utils.dispose(enumerator); }); 891 | }); 892 | } 893 | }; 894 | 895 | 896 | // Overload:function(selector) 897 | // Overload:function(selector) 898 | Enumerable.prototype.choose = function (selector) { 899 | selector = Utils.createLambda(selector); 900 | var source = this; 901 | 902 | return new Enumerable(function () { 903 | var enumerator; 904 | var index = 0; 905 | 906 | return new IEnumerator( 907 | function () { enumerator = source.getEnumerator(); }, 908 | function () { 909 | while (enumerator.moveNext()) { 910 | const result = selector(enumerator.current(), index++); 911 | if (result != null) { 912 | return this.yieldReturn(result); 913 | } 914 | } 915 | return this.yieldBreak(); 916 | }, 917 | function () { Utils.dispose(enumerator); }); 918 | }); 919 | }; 920 | 921 | Enumerable.prototype.ofType = function (type) { 922 | var typeName; 923 | switch (type) { 924 | case Number: 925 | typeName = Types.Number; 926 | break; 927 | case String: 928 | typeName = Types.String; 929 | break; 930 | case Boolean: 931 | typeName = Types.Boolean; 932 | break; 933 | case Function: 934 | typeName = Types.Function; 935 | break; 936 | default: 937 | typeName = null; 938 | break; 939 | } 940 | return (typeName === null) 941 | ? this.where(function (x) { return x instanceof type; }) 942 | : this.where(function (x) { return typeof x === typeName; }); 943 | }; 944 | 945 | // mutiple arguments, last one is selector, others are enumerable 946 | Enumerable.prototype.zip = function () { 947 | var args = arguments; 948 | var selector = Utils.createLambda(arguments[arguments.length - 1]); 949 | 950 | var source = this; 951 | // optimized case:argument is 2 952 | if (arguments.length == 2) { 953 | const second = arguments[0]; 954 | 955 | return new Enumerable(function () { 956 | var firstEnumerator; 957 | var secondEnumerator; 958 | var index = 0; 959 | 960 | return new IEnumerator( 961 | function () { 962 | firstEnumerator = source.getEnumerator(); 963 | secondEnumerator = Enumerable.from(second).getEnumerator(); 964 | }, 965 | function () { 966 | if (firstEnumerator.moveNext() && secondEnumerator.moveNext()) { 967 | return this.yieldReturn(selector(firstEnumerator.current(), secondEnumerator.current(), index++)); 968 | } 969 | return false; 970 | }, 971 | function () { 972 | try { 973 | Utils.dispose(firstEnumerator); 974 | } finally { 975 | Utils.dispose(secondEnumerator); 976 | } 977 | }); 978 | }); 979 | } 980 | else { 981 | return new Enumerable(function () { 982 | var enumerators; 983 | var index = 0; 984 | 985 | return new IEnumerator( 986 | function () { 987 | var array = Enumerable.make(source) 988 | .concat(Enumerable.from(args).takeExceptLast().select(Enumerable.from)) 989 | .select(function (x) { return x.getEnumerator() }) 990 | .toArray(); 991 | enumerators = Enumerable.from(array); 992 | }, 993 | function () { 994 | if (enumerators.all(function (x) { return x.moveNext() })) { 995 | const array = enumerators 996 | .select(function (x) { return x.current() }) 997 | .toArray(); 998 | array.push(index++); 999 | return this.yieldReturn(selector.apply(null, array)); 1000 | } 1001 | else { 1002 | return this.yieldBreak(); 1003 | } 1004 | }, 1005 | function () { 1006 | Enumerable.from(enumerators).forEach(Utils.dispose); 1007 | }); 1008 | }); 1009 | } 1010 | }; 1011 | 1012 | // mutiple arguments 1013 | Enumerable.prototype.merge = function () { 1014 | var args = arguments; 1015 | var source = this; 1016 | 1017 | return new Enumerable(function () { 1018 | var enumerators; 1019 | var index = -1; 1020 | 1021 | return new IEnumerator( 1022 | function () { 1023 | enumerators = Enumerable.make(source) 1024 | .concat(Enumerable.from(args).select(Enumerable.from)) 1025 | .select(function (x) { return x.getEnumerator() }) 1026 | .toArray(); 1027 | }, 1028 | function () { 1029 | while (enumerators.length > 0) { 1030 | index = (index >= enumerators.length - 1) ? 0 : index + 1; 1031 | const enumerator = enumerators[index]; 1032 | 1033 | if (enumerator.moveNext()) { 1034 | return this.yieldReturn(enumerator.current()); 1035 | } 1036 | else { 1037 | enumerator.dispose(); 1038 | enumerators.splice(index--, 1); 1039 | } 1040 | } 1041 | return this.yieldBreak(); 1042 | }, 1043 | function () { 1044 | Enumerable.from(enumerators).forEach(Utils.dispose); 1045 | }); 1046 | }); 1047 | }; 1048 | 1049 | //////////////// 1050 | // Join Methods 1051 | 1052 | // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) 1053 | // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) 1054 | Enumerable.prototype.join = function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { 1055 | outerKeySelector = Utils.createLambda(outerKeySelector); 1056 | innerKeySelector = Utils.createLambda(innerKeySelector); 1057 | resultSelector = Utils.createLambda(resultSelector); 1058 | compareSelector = Utils.createLambda(compareSelector); 1059 | var source = this; 1060 | 1061 | return new Enumerable(function () { 1062 | var outerEnumerator; 1063 | var lookup; 1064 | var innerElements = null; 1065 | var innerCount = 0; 1066 | 1067 | return new IEnumerator( 1068 | function () { 1069 | outerEnumerator = source.getEnumerator(); 1070 | lookup = Enumerable.from(inner).toLookup(innerKeySelector, Functions.Identity, compareSelector); 1071 | }, 1072 | function () { 1073 | while (true) { 1074 | if (innerElements != null) { 1075 | let innerElement = innerElements[innerCount++]; 1076 | if (innerElement !== undefined) { 1077 | return this.yieldReturn(resultSelector(outerEnumerator.current(), innerElement)); 1078 | } 1079 | 1080 | innerElement = null; 1081 | innerCount = 0; 1082 | } 1083 | 1084 | if (outerEnumerator.moveNext()) { 1085 | const key = outerKeySelector(outerEnumerator.current()); 1086 | innerElements = lookup.get(key).toArray(); 1087 | } else { 1088 | return false; 1089 | } 1090 | } 1091 | }, 1092 | function () { Utils.dispose(outerEnumerator); }); 1093 | }); 1094 | }; 1095 | 1096 | // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) 1097 | // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) 1098 | Enumerable.prototype.leftJoin = function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { 1099 | outerKeySelector = Utils.createLambda(outerKeySelector); 1100 | innerKeySelector = Utils.createLambda(innerKeySelector); 1101 | resultSelector = Utils.createLambda(resultSelector); 1102 | compareSelector = Utils.createLambda(compareSelector); 1103 | var source = this; 1104 | 1105 | return new Enumerable(function () { 1106 | var outerEnumerator; 1107 | var lookup; 1108 | var innerElements = null; 1109 | var innerCount = 0; 1110 | 1111 | return new IEnumerator( 1112 | function () { 1113 | outerEnumerator = source.getEnumerator(); 1114 | lookup = Enumerable.from(inner).toLookup(innerKeySelector, Functions.Identity, compareSelector); 1115 | }, 1116 | function () { 1117 | while (true) { 1118 | if (innerElements != null) { 1119 | let innerElement = innerElements[innerCount++]; 1120 | if (innerElement !== undefined) { 1121 | return this.yieldReturn(resultSelector(outerEnumerator.current(), innerElement)); 1122 | } 1123 | 1124 | innerElement = null; 1125 | innerCount = 0; 1126 | } 1127 | 1128 | if (outerEnumerator.moveNext()) { 1129 | const key = outerKeySelector(outerEnumerator.current()); 1130 | innerElements = lookup.get(key).toArray(); 1131 | // execute once if innerElements is NULL 1132 | if (innerElements == null || innerElements.length == 0) { 1133 | return this.yieldReturn(resultSelector(outerEnumerator.current(), null)); 1134 | } 1135 | } else { 1136 | return false; 1137 | } 1138 | } 1139 | }, 1140 | function () { Utils.dispose(outerEnumerator); }); 1141 | }); 1142 | }; 1143 | 1144 | // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) 1145 | // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) 1146 | Enumerable.prototype.groupJoin = function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { 1147 | outerKeySelector = Utils.createLambda(outerKeySelector); 1148 | innerKeySelector = Utils.createLambda(innerKeySelector); 1149 | resultSelector = Utils.createLambda(resultSelector); 1150 | compareSelector = Utils.createLambda(compareSelector); 1151 | var source = this; 1152 | 1153 | return new Enumerable(function () { 1154 | var enumerator = source.getEnumerator(); 1155 | var lookup = null; 1156 | 1157 | return new IEnumerator( 1158 | function () { 1159 | enumerator = source.getEnumerator(); 1160 | lookup = Enumerable.from(inner).toLookup(innerKeySelector, Functions.Identity, compareSelector); 1161 | }, 1162 | function () { 1163 | if (enumerator.moveNext()) { 1164 | const innerElement = lookup.get(outerKeySelector(enumerator.current())); 1165 | return this.yieldReturn(resultSelector(enumerator.current(), innerElement)); 1166 | } 1167 | return false; 1168 | }, 1169 | function () { Utils.dispose(enumerator); }); 1170 | }); 1171 | }; 1172 | 1173 | /////////////// 1174 | // Set Methods 1175 | 1176 | Enumerable.prototype.all = function (predicate) { 1177 | predicate = Utils.createLambda(predicate); 1178 | 1179 | var result = true; 1180 | this.forEach(function (x) { 1181 | if (!predicate(x)) { 1182 | result = false; 1183 | return false; // break 1184 | } 1185 | }); 1186 | return result; 1187 | }; 1188 | 1189 | // Overload:function() 1190 | // Overload:function(predicate) 1191 | Enumerable.prototype.any = function (predicate) { 1192 | predicate = Utils.createLambda(predicate); 1193 | 1194 | var enumerator = this.getEnumerator(); 1195 | try { 1196 | if (arguments.length == 0) return enumerator.moveNext(); // case:function() 1197 | 1198 | while (enumerator.moveNext()) // case:function(predicate) 1199 | { 1200 | if (predicate(enumerator.current())) return true; 1201 | } 1202 | return false; 1203 | } 1204 | finally { 1205 | Utils.dispose(enumerator); 1206 | } 1207 | }; 1208 | 1209 | Enumerable.prototype.isEmpty = function () { 1210 | return !this.any(); 1211 | }; 1212 | 1213 | // multiple arguments 1214 | Enumerable.prototype.concat = function () { 1215 | var source = this; 1216 | 1217 | if (arguments.length == 1) { 1218 | const second = arguments[0]; 1219 | 1220 | return new Enumerable(function () { 1221 | var firstEnumerator; 1222 | var secondEnumerator; 1223 | 1224 | return new IEnumerator( 1225 | function () { firstEnumerator = source.getEnumerator(); }, 1226 | function () { 1227 | if (secondEnumerator == null) { 1228 | if (firstEnumerator.moveNext()) return this.yieldReturn(firstEnumerator.current()); 1229 | secondEnumerator = Enumerable.from(second).getEnumerator(); 1230 | } 1231 | if (secondEnumerator.moveNext()) return this.yieldReturn(secondEnumerator.current()); 1232 | return false; 1233 | }, 1234 | function () { 1235 | try { 1236 | Utils.dispose(firstEnumerator); 1237 | } 1238 | finally { 1239 | Utils.dispose(secondEnumerator); 1240 | } 1241 | }); 1242 | }); 1243 | } 1244 | else { 1245 | const args = arguments; 1246 | 1247 | return new Enumerable(function () { 1248 | var enumerators; 1249 | 1250 | return new IEnumerator( 1251 | function () { 1252 | enumerators = Enumerable.make(source) 1253 | .concat(Enumerable.from(args).select(Enumerable.from)) 1254 | .select(function (x) { return x.getEnumerator() }) 1255 | .toArray(); 1256 | }, 1257 | function () { 1258 | while (enumerators.length > 0) { 1259 | const enumerator = enumerators[0]; 1260 | 1261 | if (enumerator.moveNext()) { 1262 | return this.yieldReturn(enumerator.current()); 1263 | } 1264 | else { 1265 | enumerator.dispose(); 1266 | enumerators.splice(0, 1); 1267 | } 1268 | } 1269 | return this.yieldBreak(); 1270 | }, 1271 | function () { 1272 | Enumerable.from(enumerators).forEach(Utils.dispose); 1273 | }); 1274 | }); 1275 | } 1276 | }; 1277 | 1278 | Enumerable.prototype.insert = function (index, second) { 1279 | var source = this; 1280 | 1281 | return new Enumerable(function () { 1282 | var firstEnumerator; 1283 | var secondEnumerator; 1284 | var count = 0; 1285 | var isEnumerated = false; 1286 | 1287 | return new IEnumerator( 1288 | function () { 1289 | firstEnumerator = source.getEnumerator(); 1290 | secondEnumerator = Enumerable.from(second).getEnumerator(); 1291 | }, 1292 | function () { 1293 | if (count == index && secondEnumerator.moveNext()) { 1294 | isEnumerated = true; 1295 | return this.yieldReturn(secondEnumerator.current()); 1296 | } 1297 | if (firstEnumerator.moveNext()) { 1298 | count++; 1299 | return this.yieldReturn(firstEnumerator.current()); 1300 | } 1301 | if (!isEnumerated && secondEnumerator.moveNext()) { 1302 | return this.yieldReturn(secondEnumerator.current()); 1303 | } 1304 | return false; 1305 | }, 1306 | function () { 1307 | try { 1308 | Utils.dispose(firstEnumerator); 1309 | } 1310 | finally { 1311 | Utils.dispose(secondEnumerator); 1312 | } 1313 | }); 1314 | }); 1315 | }; 1316 | 1317 | Enumerable.prototype.alternate = function (alternateValueOrSequence) { 1318 | var source = this; 1319 | 1320 | return new Enumerable(function () { 1321 | var buffer; 1322 | var enumerator; 1323 | var alternateSequence; 1324 | var alternateEnumerator; 1325 | 1326 | return new IEnumerator( 1327 | function () { 1328 | if (alternateValueOrSequence instanceof Array || alternateValueOrSequence.getEnumerator != null) { 1329 | alternateSequence = Enumerable.from(Enumerable.from(alternateValueOrSequence).toArray()); // freeze 1330 | } 1331 | else { 1332 | alternateSequence = Enumerable.make(alternateValueOrSequence); 1333 | } 1334 | enumerator = source.getEnumerator(); 1335 | if (enumerator.moveNext()) buffer = enumerator.current(); 1336 | }, 1337 | function () { 1338 | while (true) { 1339 | if (alternateEnumerator != null) { 1340 | if (alternateEnumerator.moveNext()) { 1341 | return this.yieldReturn(alternateEnumerator.current()); 1342 | } 1343 | else { 1344 | alternateEnumerator = null; 1345 | } 1346 | } 1347 | 1348 | if (buffer == null && enumerator.moveNext()) { 1349 | buffer = enumerator.current(); // hasNext 1350 | alternateEnumerator = alternateSequence.getEnumerator(); 1351 | continue; // GOTO 1352 | } 1353 | else if (buffer != null) { 1354 | const retVal = buffer; 1355 | buffer = null; 1356 | return this.yieldReturn(retVal); 1357 | } 1358 | 1359 | return this.yieldBreak(); 1360 | } 1361 | }, 1362 | function () { 1363 | try { 1364 | Utils.dispose(enumerator); 1365 | } 1366 | finally { 1367 | Utils.dispose(alternateEnumerator); 1368 | } 1369 | }); 1370 | }); 1371 | }; 1372 | 1373 | // Overload:function(value) 1374 | // Overload:function(value, compareSelector) 1375 | Enumerable.prototype.contains = function (value, compareSelector) { 1376 | compareSelector = Utils.createLambda(compareSelector); 1377 | var enumerator = this.getEnumerator(); 1378 | try { 1379 | while (enumerator.moveNext()) { 1380 | if (compareSelector(enumerator.current()) === value) return true; 1381 | } 1382 | return false; 1383 | } 1384 | finally { 1385 | Utils.dispose(enumerator); 1386 | } 1387 | }; 1388 | 1389 | Enumerable.prototype.defaultIfEmpty = function (defaultValue) { 1390 | var source = this; 1391 | if (defaultValue === undefined) defaultValue = null; 1392 | 1393 | return new Enumerable(function () { 1394 | var enumerator; 1395 | var isFirst = true; 1396 | 1397 | return new IEnumerator( 1398 | function () { enumerator = source.getEnumerator(); }, 1399 | function () { 1400 | if (enumerator.moveNext()) { 1401 | isFirst = false; 1402 | return this.yieldReturn(enumerator.current()); 1403 | } 1404 | else if (isFirst) { 1405 | isFirst = false; 1406 | return this.yieldReturn(defaultValue); 1407 | } 1408 | return false; 1409 | }, 1410 | function () { Utils.dispose(enumerator); }); 1411 | }); 1412 | }; 1413 | 1414 | // Overload:function() 1415 | // Overload:function(compareSelector) 1416 | Enumerable.prototype.distinct = function (compareSelector) { 1417 | return this.except(Enumerable.empty(), compareSelector); 1418 | }; 1419 | 1420 | Enumerable.prototype.distinctUntilChanged = function (compareSelector) { 1421 | compareSelector = Utils.createLambda(compareSelector); 1422 | var source = this; 1423 | 1424 | return new Enumerable(function () { 1425 | var enumerator; 1426 | var compareKey; 1427 | var initial; 1428 | 1429 | return new IEnumerator( 1430 | function () { 1431 | enumerator = source.getEnumerator(); 1432 | }, 1433 | function () { 1434 | while (enumerator.moveNext()) { 1435 | const key = compareSelector(enumerator.current()); 1436 | 1437 | if (initial) { 1438 | initial = false; 1439 | compareKey = key; 1440 | return this.yieldReturn(enumerator.current()); 1441 | } 1442 | 1443 | if (compareKey === key) { 1444 | continue; 1445 | } 1446 | 1447 | compareKey = key; 1448 | return this.yieldReturn(enumerator.current()); 1449 | } 1450 | return this.yieldBreak(); 1451 | }, 1452 | function () { Utils.dispose(enumerator); }); 1453 | }); 1454 | }; 1455 | 1456 | // Overload:function(second) 1457 | // Overload:function(second, compareSelector) 1458 | Enumerable.prototype.except = function (second, compareSelector) { 1459 | compareSelector = Utils.createLambda(compareSelector); 1460 | var source = this; 1461 | 1462 | return new Enumerable(function () { 1463 | var enumerator; 1464 | var keys; 1465 | 1466 | return new IEnumerator( 1467 | function () { 1468 | enumerator = source.getEnumerator(); 1469 | keys = new Dictionary(compareSelector); 1470 | Enumerable.from(second).forEach(function (key) { keys.add(key); }); 1471 | }, 1472 | function () { 1473 | while (enumerator.moveNext()) { 1474 | const current = enumerator.current(); 1475 | if (!keys.contains(current)) { 1476 | keys.add(current); 1477 | return this.yieldReturn(current); 1478 | } 1479 | } 1480 | return false; 1481 | }, 1482 | function () { Utils.dispose(enumerator); }); 1483 | }); 1484 | }; 1485 | 1486 | // Overload:function(second) 1487 | // Overload:function(second, compareSelector) 1488 | Enumerable.prototype.intersect = function (second, compareSelector) { 1489 | compareSelector = Utils.createLambda(compareSelector); 1490 | var source = this; 1491 | 1492 | return new Enumerable(function () { 1493 | var enumerator; 1494 | var keys; 1495 | var outs; 1496 | 1497 | return new IEnumerator( 1498 | function () { 1499 | enumerator = source.getEnumerator(); 1500 | 1501 | keys = new Dictionary(compareSelector); 1502 | Enumerable.from(second).forEach(function (key) { keys.add(key); }); 1503 | outs = new Dictionary(compareSelector); 1504 | }, 1505 | function () { 1506 | while (enumerator.moveNext()) { 1507 | const current = enumerator.current(); 1508 | if (!outs.contains(current) && keys.contains(current)) { 1509 | outs.add(current); 1510 | return this.yieldReturn(current); 1511 | } 1512 | } 1513 | return false; 1514 | }, 1515 | function () { Utils.dispose(enumerator); }); 1516 | }); 1517 | }; 1518 | 1519 | // Overload:function(second) 1520 | // Overload:function(second, compareSelector) 1521 | Enumerable.prototype.sequenceEqual = function (second, compareSelector) { 1522 | compareSelector = Utils.createLambda(compareSelector); 1523 | 1524 | var firstEnumerator = this.getEnumerator(); 1525 | try { 1526 | const secondEnumerator = Enumerable.from(second).getEnumerator(); 1527 | try { 1528 | while (firstEnumerator.moveNext()) { 1529 | if (!secondEnumerator.moveNext() 1530 | || compareSelector(firstEnumerator.current()) !== compareSelector(secondEnumerator.current())) { 1531 | return false; 1532 | } 1533 | } 1534 | 1535 | if (secondEnumerator.moveNext()) return false; 1536 | return true; 1537 | } 1538 | finally { 1539 | Utils.dispose(secondEnumerator); 1540 | } 1541 | } 1542 | finally { 1543 | Utils.dispose(firstEnumerator); 1544 | } 1545 | }; 1546 | 1547 | Enumerable.prototype.union = function (second, compareSelector) { 1548 | compareSelector = Utils.createLambda(compareSelector); 1549 | var source = this; 1550 | 1551 | return new Enumerable(function () { 1552 | var firstEnumerator; 1553 | var secondEnumerator; 1554 | var keys; 1555 | 1556 | return new IEnumerator( 1557 | function () { 1558 | firstEnumerator = source.getEnumerator(); 1559 | keys = new Dictionary(compareSelector); 1560 | }, 1561 | function () { 1562 | var current; 1563 | if (secondEnumerator === undefined) { 1564 | while (firstEnumerator.moveNext()) { 1565 | current = firstEnumerator.current(); 1566 | if (!keys.contains(current)) { 1567 | keys.add(current); 1568 | return this.yieldReturn(current); 1569 | } 1570 | } 1571 | secondEnumerator = Enumerable.from(second).getEnumerator(); 1572 | } 1573 | while (secondEnumerator.moveNext()) { 1574 | current = secondEnumerator.current(); 1575 | if (!keys.contains(current)) { 1576 | keys.add(current); 1577 | return this.yieldReturn(current); 1578 | } 1579 | } 1580 | return false; 1581 | }, 1582 | function () { 1583 | try { 1584 | Utils.dispose(firstEnumerator); 1585 | } 1586 | finally { 1587 | Utils.dispose(secondEnumerator); 1588 | } 1589 | }); 1590 | }); 1591 | }; 1592 | 1593 | //////////////////// 1594 | // Ordering Methods 1595 | 1596 | Enumerable.prototype.orderBy = function (keySelector, comparer) { 1597 | return new OrderedEnumerable(this, keySelector, comparer, false); 1598 | }; 1599 | 1600 | Enumerable.prototype.orderByDescending = function (keySelector, comparer) { 1601 | return new OrderedEnumerable(this, keySelector, comparer, true); 1602 | }; 1603 | 1604 | Enumerable.prototype.reverse = function () { 1605 | var source = this; 1606 | 1607 | return new Enumerable(function () { 1608 | var buffer; 1609 | var index; 1610 | 1611 | return new IEnumerator( 1612 | function () { 1613 | buffer = source.toArray(); 1614 | index = buffer.length; 1615 | }, 1616 | function () { 1617 | return (index > 0) 1618 | ? this.yieldReturn(buffer[--index]) 1619 | : false; 1620 | }, 1621 | Functions.Blank); 1622 | }); 1623 | }; 1624 | 1625 | Enumerable.prototype.shuffle = function () { 1626 | var source = this; 1627 | 1628 | return new Enumerable(function () { 1629 | var buffer; 1630 | 1631 | return new IEnumerator( 1632 | function () { buffer = source.toArray(); }, 1633 | function () { 1634 | if (buffer.length > 0) { 1635 | const i = Math.floor(Math.random() * buffer.length); 1636 | return this.yieldReturn(buffer.splice(i, 1)[0]); 1637 | } 1638 | return false; 1639 | }, 1640 | Functions.Blank); 1641 | }); 1642 | }; 1643 | 1644 | Enumerable.prototype.weightedSample = function (weightSelector) { 1645 | weightSelector = Utils.createLambda(weightSelector); 1646 | var source = this; 1647 | 1648 | return new Enumerable(function () { 1649 | var sortedByBound; 1650 | var totalWeight = 0; 1651 | 1652 | return new IEnumerator( 1653 | function () { 1654 | sortedByBound = source 1655 | .choose(function (x) { 1656 | var weight = weightSelector(x); 1657 | if (weight <= 0) return null; // ignore 0 1658 | 1659 | totalWeight += weight; 1660 | return { value: x, bound: totalWeight }; 1661 | }) 1662 | .toArray(); 1663 | }, 1664 | function () { 1665 | if (sortedByBound.length > 0) { 1666 | const draw = Math.floor(Math.random() * totalWeight) + 1; 1667 | 1668 | let lower = -1; 1669 | let upper = sortedByBound.length; 1670 | while (upper - lower > 1) { 1671 | const index = Math.floor((lower + upper) / 2); 1672 | if (sortedByBound[index].bound >= draw) { 1673 | upper = index; 1674 | } 1675 | else { 1676 | lower = index; 1677 | } 1678 | } 1679 | 1680 | return this.yieldReturn(sortedByBound[upper].value); 1681 | } 1682 | 1683 | return this.yieldBreak(); 1684 | }, 1685 | Functions.Blank); 1686 | }); 1687 | }; 1688 | 1689 | //////////////////// 1690 | // Grouping Methods 1691 | 1692 | // Overload:function(keySelector) 1693 | // Overload:function(keySelector,elementSelector) 1694 | // Overload:function(keySelector,elementSelector,resultSelector) 1695 | // Overload:function(keySelector,elementSelector,resultSelector,compareSelector) 1696 | Enumerable.prototype.groupBy = function (keySelector, elementSelector, resultSelector, compareSelector) { 1697 | var source = this; 1698 | keySelector = Utils.createLambda(keySelector); 1699 | elementSelector = Utils.createLambda(elementSelector); 1700 | if (resultSelector != null) resultSelector = Utils.createLambda(resultSelector); 1701 | compareSelector = Utils.createLambda(compareSelector); 1702 | 1703 | return new Enumerable(function () { 1704 | var enumerator; 1705 | 1706 | return new IEnumerator( 1707 | function () { 1708 | enumerator = source.toLookup(keySelector, elementSelector, compareSelector) 1709 | .toEnumerable() 1710 | .getEnumerator(); 1711 | }, 1712 | function () { 1713 | while (enumerator.moveNext()) { 1714 | return (resultSelector == null) 1715 | ? this.yieldReturn(enumerator.current()) 1716 | : this.yieldReturn(resultSelector(enumerator.current().key(), enumerator.current())); 1717 | } 1718 | return false; 1719 | }, 1720 | function () { Utils.dispose(enumerator); }); 1721 | }); 1722 | }; 1723 | 1724 | // Overload:function(keySelector) 1725 | // Overload:function(keySelector,elementSelector) 1726 | // Overload:function(keySelector,elementSelector,resultSelector) 1727 | // Overload:function(keySelector,elementSelector,resultSelector,compareSelector) 1728 | Enumerable.prototype.partitionBy = function (keySelector, elementSelector, resultSelector, compareSelector) { 1729 | var source = this; 1730 | keySelector = Utils.createLambda(keySelector); 1731 | elementSelector = Utils.createLambda(elementSelector); 1732 | compareSelector = Utils.createLambda(compareSelector); 1733 | var hasResultSelector; 1734 | if (resultSelector == null) { 1735 | hasResultSelector = false; 1736 | resultSelector = function (key, group) { return new Grouping(key, group); }; 1737 | } 1738 | else { 1739 | hasResultSelector = true; 1740 | resultSelector = Utils.createLambda(resultSelector); 1741 | } 1742 | 1743 | return new Enumerable(function () { 1744 | var enumerator; 1745 | var key; 1746 | var compareKey; 1747 | var group = []; 1748 | 1749 | return new IEnumerator( 1750 | function () { 1751 | enumerator = source.getEnumerator(); 1752 | if (enumerator.moveNext()) { 1753 | key = keySelector(enumerator.current()); 1754 | compareKey = compareSelector(key); 1755 | group.push(elementSelector(enumerator.current())); 1756 | } 1757 | }, 1758 | function () { 1759 | var hasNext; 1760 | while ((hasNext = enumerator.moveNext()) == true) { 1761 | if (compareKey === compareSelector(keySelector(enumerator.current()))) { 1762 | group.push(elementSelector(enumerator.current())); 1763 | } 1764 | else break; 1765 | } 1766 | 1767 | if (group.length > 0) { 1768 | const result = (hasResultSelector) 1769 | ? resultSelector(key, Enumerable.from(group)) 1770 | : resultSelector(key, group); 1771 | if (hasNext) { 1772 | key = keySelector(enumerator.current()); 1773 | compareKey = compareSelector(key); 1774 | group = [elementSelector(enumerator.current())]; 1775 | } 1776 | else group = []; 1777 | 1778 | return this.yieldReturn(result); 1779 | } 1780 | 1781 | return false; 1782 | }, 1783 | function () { Utils.dispose(enumerator); }); 1784 | }); 1785 | }; 1786 | 1787 | Enumerable.prototype.buffer = function (count) { 1788 | var source = this; 1789 | 1790 | return new Enumerable(function () { 1791 | var enumerator; 1792 | 1793 | return new IEnumerator( 1794 | function () { enumerator = source.getEnumerator(); }, 1795 | function () { 1796 | var array = []; 1797 | var index = 0; 1798 | while (enumerator.moveNext()) { 1799 | array.push(enumerator.current()); 1800 | if (++index >= count) return this.yieldReturn(array); 1801 | } 1802 | if (array.length > 0) return this.yieldReturn(array); 1803 | return false; 1804 | }, 1805 | function () { Utils.dispose(enumerator); }); 1806 | }); 1807 | }; 1808 | 1809 | ///////////////////// 1810 | // Aggregate Methods 1811 | 1812 | // Overload:function(func) 1813 | // Overload:function(seed,func) 1814 | // Overload:function(seed,func,resultSelector) 1815 | Enumerable.prototype.aggregate = function (seed, func, resultSelector) { 1816 | resultSelector = Utils.createLambda(resultSelector); 1817 | return resultSelector(this.scan(seed, func, resultSelector).last()); 1818 | }; 1819 | 1820 | // Overload:function() 1821 | // Overload:function(selector) 1822 | Enumerable.prototype.average = function (selector) { 1823 | selector = Utils.createLambda(selector); 1824 | 1825 | var sum = 0; 1826 | var count = 0; 1827 | this.forEach(function (x) { 1828 | sum += selector(x); 1829 | ++count; 1830 | }); 1831 | 1832 | return sum / count; 1833 | }; 1834 | 1835 | // Overload:function() 1836 | // Overload:function(predicate) 1837 | Enumerable.prototype.count = function (predicate) { 1838 | predicate = (predicate == null) ? Functions.True : Utils.createLambda(predicate); 1839 | 1840 | var count = 0; 1841 | this.forEach(function (x, i) { 1842 | if (predicate(x, i)) ++count; 1843 | }); 1844 | return count; 1845 | }; 1846 | 1847 | // Overload:function() 1848 | // Overload:function(selector) 1849 | Enumerable.prototype.max = function (selector) { 1850 | if (selector == null) selector = Functions.Identity; 1851 | return this.select(selector).aggregate(function (a, b) { return (a > b) ? a : b; }); 1852 | }; 1853 | 1854 | // Overload:function() 1855 | // Overload:function(selector) 1856 | Enumerable.prototype.min = function (selector) { 1857 | if (selector == null) selector = Functions.Identity; 1858 | return this.select(selector).aggregate(function (a, b) { return (a < b) ? a : b; }); 1859 | }; 1860 | 1861 | Enumerable.prototype.maxBy = function (keySelector) { 1862 | keySelector = Utils.createLambda(keySelector); 1863 | return this.aggregate(function (a, b) { return (keySelector(a) > keySelector(b)) ? a : b; }); 1864 | }; 1865 | 1866 | Enumerable.prototype.minBy = function (keySelector) { 1867 | keySelector = Utils.createLambda(keySelector); 1868 | return this.aggregate(function (a, b) { return (keySelector(a) < keySelector(b)) ? a : b; }); 1869 | }; 1870 | 1871 | // Overload:function() 1872 | // Overload:function(selector) 1873 | Enumerable.prototype.sum = function (selector) { 1874 | if (selector == null) selector = Functions.Identity; 1875 | return this.select(selector).aggregate(0, function (a, b) { return a + b; }); 1876 | }; 1877 | 1878 | ////////////////// 1879 | // Paging Methods 1880 | 1881 | Enumerable.prototype.elementAt = function (index) { 1882 | var value; 1883 | var found = false; 1884 | this.forEach(function (x, i) { 1885 | if (i == index) { 1886 | value = x; 1887 | found = true; 1888 | return false; 1889 | } 1890 | }); 1891 | 1892 | if (!found) throw new Error("index is less than 0 or greater than or equal to the number of elements in source."); 1893 | return value; 1894 | }; 1895 | 1896 | Enumerable.prototype.elementAtOrDefault = function (index, defaultValue) { 1897 | if (defaultValue === undefined) defaultValue = null; 1898 | var value; 1899 | var found = false; 1900 | this.forEach(function (x, i) { 1901 | if (i == index) { 1902 | value = x; 1903 | found = true; 1904 | return false; 1905 | } 1906 | }); 1907 | 1908 | return (!found) ? defaultValue : value; 1909 | }; 1910 | 1911 | // Overload:function() 1912 | // Overload:function(predicate) 1913 | Enumerable.prototype.first = function (predicate) { 1914 | if (predicate != null) return this.where(predicate).first(); 1915 | 1916 | var value; 1917 | var found = false; 1918 | this.forEach(function (x) { 1919 | value = x; 1920 | found = true; 1921 | return false; 1922 | }); 1923 | 1924 | if (!found) throw new Error("first:No element satisfies the condition."); 1925 | return value; 1926 | }; 1927 | 1928 | Enumerable.prototype.firstOrDefault = function (predicate, defaultValue) { 1929 | if (predicate !== undefined) { 1930 | if (typeof predicate === Types.Function || typeof Utils.createLambda(predicate) === Types.Function) { 1931 | return this.where(predicate).firstOrDefault(undefined, defaultValue); 1932 | } 1933 | defaultValue = predicate; 1934 | } 1935 | 1936 | var value; 1937 | var found = false; 1938 | this.forEach(function (x) { 1939 | value = x; 1940 | found = true; 1941 | return false; 1942 | }); 1943 | return (!found) ? defaultValue : value; 1944 | }; 1945 | 1946 | // Overload:function() 1947 | // Overload:function(predicate) 1948 | Enumerable.prototype.last = function (predicate) { 1949 | if (predicate != null) return this.where(predicate).last(); 1950 | 1951 | var value; 1952 | var found = false; 1953 | this.forEach(function (x) { 1954 | found = true; 1955 | value = x; 1956 | }); 1957 | 1958 | if (!found) throw new Error("last:No element satisfies the condition."); 1959 | return value; 1960 | }; 1961 | 1962 | Enumerable.prototype.lastOrDefault = function (predicate, defaultValue) { 1963 | if (predicate !== undefined) { 1964 | if (typeof predicate === Types.Function || typeof Utils.createLambda(predicate) === Types.Function) { 1965 | return this.where(predicate).lastOrDefault(undefined, defaultValue); 1966 | } 1967 | defaultValue = predicate; 1968 | } 1969 | 1970 | var value; 1971 | var found = false; 1972 | this.forEach(function (x) { 1973 | found = true; 1974 | value = x; 1975 | }); 1976 | return (!found) ? defaultValue : value; 1977 | }; 1978 | 1979 | // Overload:function() 1980 | // Overload:function(predicate) 1981 | Enumerable.prototype.single = function (predicate) { 1982 | if (predicate != null) return this.where(predicate).single(); 1983 | 1984 | var value; 1985 | var found = false; 1986 | this.forEach(function (x) { 1987 | if (!found) { 1988 | found = true; 1989 | value = x; 1990 | } else throw new Error("single:sequence contains more than one element."); 1991 | }); 1992 | 1993 | if (!found) throw new Error("single:No element satisfies the condition."); 1994 | return value; 1995 | }; 1996 | 1997 | // Overload:function(defaultValue) 1998 | // Overload:function(defaultValue,predicate) 1999 | Enumerable.prototype.singleOrDefault = function (predicate, defaultValue) { 2000 | if (defaultValue === undefined) defaultValue = null; 2001 | if (predicate != null) return this.where(predicate).singleOrDefault(null, defaultValue); 2002 | 2003 | var value; 2004 | var found = false; 2005 | this.forEach(function (x) { 2006 | if (!found) { 2007 | found = true; 2008 | value = x; 2009 | } else throw new Error("single:sequence contains more than one element."); 2010 | }); 2011 | 2012 | return (!found) ? defaultValue : value; 2013 | }; 2014 | 2015 | Enumerable.prototype.skip = function (count) { 2016 | var source = this; 2017 | 2018 | return new Enumerable(function () { 2019 | var enumerator; 2020 | var index = 0; 2021 | 2022 | return new IEnumerator( 2023 | function () { 2024 | enumerator = source.getEnumerator(); 2025 | while (index++ < count && enumerator.moveNext()) { } 2026 | }, 2027 | function () { 2028 | return (enumerator.moveNext()) 2029 | ? this.yieldReturn(enumerator.current()) 2030 | : false; 2031 | }, 2032 | function () { Utils.dispose(enumerator); }); 2033 | }); 2034 | }; 2035 | 2036 | // Overload:function(predicate) 2037 | // Overload:function(predicate) 2038 | Enumerable.prototype.skipWhile = function (predicate) { 2039 | predicate = Utils.createLambda(predicate); 2040 | var source = this; 2041 | 2042 | return new Enumerable(function () { 2043 | var enumerator; 2044 | var index = 0; 2045 | var isSkipEnd = false; 2046 | 2047 | return new IEnumerator( 2048 | function () { enumerator = source.getEnumerator(); }, 2049 | function () { 2050 | while (!isSkipEnd) { 2051 | if (enumerator.moveNext()) { 2052 | if (!predicate(enumerator.current(), index++)) { 2053 | isSkipEnd = true; 2054 | return this.yieldReturn(enumerator.current()); 2055 | } 2056 | continue; 2057 | } else return false; 2058 | } 2059 | 2060 | return (enumerator.moveNext()) 2061 | ? this.yieldReturn(enumerator.current()) 2062 | : false; 2063 | 2064 | }, 2065 | function () { Utils.dispose(enumerator); }); 2066 | }); 2067 | }; 2068 | 2069 | Enumerable.prototype.take = function (count) { 2070 | var source = this; 2071 | 2072 | return new Enumerable(function () { 2073 | var enumerator; 2074 | var index = 0; 2075 | 2076 | return new IEnumerator( 2077 | function () { enumerator = source.getEnumerator(); }, 2078 | function () { 2079 | return (index++ < count && enumerator.moveNext()) 2080 | ? this.yieldReturn(enumerator.current()) 2081 | : false; 2082 | }, 2083 | function () { Utils.dispose(enumerator); } 2084 | ); 2085 | }); 2086 | }; 2087 | 2088 | // Overload:function(predicate) 2089 | // Overload:function(predicate) 2090 | Enumerable.prototype.takeWhile = function (predicate) { 2091 | predicate = Utils.createLambda(predicate); 2092 | var source = this; 2093 | 2094 | return new Enumerable(function () { 2095 | var enumerator; 2096 | var index = 0; 2097 | 2098 | return new IEnumerator( 2099 | function () { enumerator = source.getEnumerator(); }, 2100 | function () { 2101 | return (enumerator.moveNext() && predicate(enumerator.current(), index++)) 2102 | ? this.yieldReturn(enumerator.current()) 2103 | : false; 2104 | }, 2105 | function () { Utils.dispose(enumerator); }); 2106 | }); 2107 | }; 2108 | 2109 | // Overload:function() 2110 | // Overload:function(count) 2111 | Enumerable.prototype.takeExceptLast = function (count) { 2112 | if (count == null) count = 1; 2113 | var source = this; 2114 | 2115 | return new Enumerable(function () { 2116 | if (count <= 0) return source.getEnumerator(); // do nothing 2117 | 2118 | var enumerator; 2119 | var q = []; 2120 | 2121 | return new IEnumerator( 2122 | function () { enumerator = source.getEnumerator(); }, 2123 | function () { 2124 | while (enumerator.moveNext()) { 2125 | if (q.length == count) { 2126 | q.push(enumerator.current()); 2127 | return this.yieldReturn(q.shift()); 2128 | } 2129 | q.push(enumerator.current()); 2130 | } 2131 | return false; 2132 | }, 2133 | function () { Utils.dispose(enumerator); }); 2134 | }); 2135 | }; 2136 | 2137 | Enumerable.prototype.takeFromLast = function (count) { 2138 | if (count <= 0 || count == null) return Enumerable.empty(); 2139 | var source = this; 2140 | 2141 | return new Enumerable(function () { 2142 | var sourceEnumerator; 2143 | var enumerator; 2144 | var q = []; 2145 | 2146 | return new IEnumerator( 2147 | function () { sourceEnumerator = source.getEnumerator(); }, 2148 | function () { 2149 | while (sourceEnumerator.moveNext()) { 2150 | if (q.length == count) q.shift(); 2151 | q.push(sourceEnumerator.current()); 2152 | } 2153 | if (enumerator == null) { 2154 | enumerator = Enumerable.from(q).getEnumerator(); 2155 | } 2156 | return (enumerator.moveNext()) 2157 | ? this.yieldReturn(enumerator.current()) 2158 | : false; 2159 | }, 2160 | function () { Utils.dispose(enumerator); }); 2161 | }); 2162 | }; 2163 | 2164 | // Overload:function(item) 2165 | // Overload:function(predicate) 2166 | Enumerable.prototype.indexOf = function (item) { 2167 | var found = null; 2168 | 2169 | // item as predicate 2170 | if (typeof (item) === Types.Function) { 2171 | this.forEach(function (x, i) { 2172 | if (item(x, i)) { 2173 | found = i; 2174 | return false; 2175 | } 2176 | }); 2177 | } 2178 | else { 2179 | this.forEach(function (x, i) { 2180 | if (x === item) { 2181 | found = i; 2182 | return false; 2183 | } 2184 | }); 2185 | } 2186 | 2187 | return (found !== null) ? found : -1; 2188 | }; 2189 | 2190 | // Overload:function(item) 2191 | // Overload:function(predicate) 2192 | Enumerable.prototype.lastIndexOf = function (item) { 2193 | var result = -1; 2194 | 2195 | // item as predicate 2196 | if (typeof (item) === Types.Function) { 2197 | this.forEach(function (x, i) { 2198 | if (item(x, i)) result = i; 2199 | }); 2200 | } 2201 | else { 2202 | this.forEach(function (x, i) { 2203 | if (x === item) result = i; 2204 | }); 2205 | } 2206 | 2207 | return result; 2208 | }; 2209 | 2210 | /////////////////// 2211 | // Convert Methods 2212 | 2213 | Enumerable.prototype.cast = function () { 2214 | return this; 2215 | }; 2216 | 2217 | Enumerable.prototype.asEnumerable = function () { 2218 | return Enumerable.from(this); 2219 | }; 2220 | 2221 | Enumerable.prototype.toArray = function () { 2222 | var array = []; 2223 | this.forEach(function (x) { array.push(x); }); 2224 | return array; 2225 | }; 2226 | 2227 | // Overload:function(keySelector) 2228 | // Overload:function(keySelector, elementSelector) 2229 | // Overload:function(keySelector, elementSelector, compareSelector) 2230 | Enumerable.prototype.toLookup = function (keySelector, elementSelector, compareSelector) { 2231 | keySelector = Utils.createLambda(keySelector); 2232 | elementSelector = Utils.createLambda(elementSelector); 2233 | compareSelector = Utils.createLambda(compareSelector); 2234 | 2235 | var dict = new Dictionary(compareSelector); 2236 | this.forEach(function (x) { 2237 | var key = keySelector(x); 2238 | var element = elementSelector(x); 2239 | 2240 | var array = dict.get(key); 2241 | if (array !== undefined) array.push(element); 2242 | else dict.add(key, [element]); 2243 | }); 2244 | return new Lookup(dict); 2245 | }; 2246 | 2247 | Enumerable.prototype.toObject = function (keySelector, elementSelector) { 2248 | keySelector = Utils.createLambda(keySelector); 2249 | elementSelector = Utils.createLambda(elementSelector); 2250 | 2251 | var obj = {}; 2252 | this.forEach(function (x) { 2253 | obj[keySelector(x)] = elementSelector(x); 2254 | }); 2255 | return obj; 2256 | }; 2257 | 2258 | // Overload:function(keySelector, elementSelector) 2259 | // Overload:function(keySelector, elementSelector, compareSelector) 2260 | Enumerable.prototype.toDictionary = function (keySelector, elementSelector, compareSelector) { 2261 | keySelector = Utils.createLambda(keySelector); 2262 | elementSelector = Utils.createLambda(elementSelector); 2263 | compareSelector = Utils.createLambda(compareSelector); 2264 | 2265 | var dict = new Dictionary(compareSelector); 2266 | this.forEach(function (x) { 2267 | dict.add(keySelector(x), elementSelector(x)); 2268 | }); 2269 | return dict; 2270 | }; 2271 | 2272 | // Overload:function() 2273 | // Overload:function(replacer) 2274 | // Overload:function(replacer, space) 2275 | Enumerable.prototype.toJSONString = function (replacer, space) { 2276 | if (typeof JSON === Types.Undefined || JSON.stringify == null) { 2277 | throw new Error("toJSONString can't find JSON.stringify. This works native JSON support Browser or include json2.js"); 2278 | } 2279 | return JSON.stringify(this.toArray(), replacer, space); 2280 | }; 2281 | 2282 | // Overload:function() 2283 | // Overload:function(separator) 2284 | // Overload:function(separator,selector) 2285 | Enumerable.prototype.toJoinedString = function (separator, selector) { 2286 | if (separator == null) separator = ""; 2287 | if (selector == null) selector = Functions.Identity; 2288 | 2289 | return this.select(selector).toArray().join(separator); 2290 | }; 2291 | 2292 | ////////////////// 2293 | // Action Methods 2294 | 2295 | // Overload:function(action) 2296 | // Overload:function(action) 2297 | Enumerable.prototype.doAction = function (action) { 2298 | var source = this; 2299 | action = Utils.createLambda(action); 2300 | 2301 | return new Enumerable(function () { 2302 | var enumerator; 2303 | var index = 0; 2304 | 2305 | return new IEnumerator( 2306 | function () { enumerator = source.getEnumerator(); }, 2307 | function () { 2308 | if (enumerator.moveNext()) { 2309 | action(enumerator.current(), index++); 2310 | return this.yieldReturn(enumerator.current()); 2311 | } 2312 | return false; 2313 | }, 2314 | function () { Utils.dispose(enumerator); }); 2315 | }); 2316 | }; 2317 | 2318 | // Overload:function(action) 2319 | // Overload:function(action) 2320 | // Overload:function(func) 2321 | // Overload:function(func) 2322 | Enumerable.prototype.forEach = function (action) { 2323 | action = Utils.createLambda(action); 2324 | 2325 | var index = 0; 2326 | var enumerator = this.getEnumerator(); 2327 | try { 2328 | while (enumerator.moveNext()) { 2329 | if (action(enumerator.current(), index++) === false) break; 2330 | } 2331 | } finally { 2332 | Utils.dispose(enumerator); 2333 | } 2334 | }; 2335 | 2336 | Enumerable.prototype.force = function () { 2337 | var enumerator = this.getEnumerator(); 2338 | 2339 | try { 2340 | while (enumerator.moveNext()) { } 2341 | } 2342 | finally { 2343 | Utils.dispose(enumerator); 2344 | } 2345 | }; 2346 | 2347 | ////////////////////// 2348 | // Functional Methods 2349 | 2350 | Enumerable.prototype.letBind = function (func) { 2351 | func = Utils.createLambda(func); 2352 | var source = this; 2353 | 2354 | return new Enumerable(function () { 2355 | var enumerator; 2356 | 2357 | return new IEnumerator( 2358 | function () { 2359 | enumerator = Enumerable.from(func(source)).getEnumerator(); 2360 | }, 2361 | function () { 2362 | return (enumerator.moveNext()) 2363 | ? this.yieldReturn(enumerator.current()) 2364 | : false; 2365 | }, 2366 | function () { Utils.dispose(enumerator); }); 2367 | }); 2368 | }; 2369 | 2370 | Enumerable.prototype.share = function () { 2371 | var source = this; 2372 | var sharedEnumerator; 2373 | var disposed = false; 2374 | 2375 | return new DisposableEnumerable(function () { 2376 | return new IEnumerator( 2377 | function () { 2378 | if (sharedEnumerator == null) { 2379 | sharedEnumerator = source.getEnumerator(); 2380 | } 2381 | }, 2382 | function () { 2383 | if (disposed) throw new Error("enumerator is disposed"); 2384 | 2385 | return (sharedEnumerator.moveNext()) 2386 | ? this.yieldReturn(sharedEnumerator.current()) 2387 | : false; 2388 | }, 2389 | Functions.Blank 2390 | ); 2391 | }, function () { 2392 | disposed = true; 2393 | Utils.dispose(sharedEnumerator); 2394 | }); 2395 | }; 2396 | 2397 | Enumerable.prototype.memoize = function () { 2398 | var source = this; 2399 | var cache; 2400 | var enumerator; 2401 | var disposed = false; 2402 | 2403 | return new DisposableEnumerable(function () { 2404 | var index = -1; 2405 | 2406 | return new IEnumerator( 2407 | function () { 2408 | if (enumerator == null) { 2409 | enumerator = source.getEnumerator(); 2410 | cache = []; 2411 | } 2412 | }, 2413 | function () { 2414 | if (disposed) throw new Error("enumerator is disposed"); 2415 | 2416 | index++; 2417 | if (cache.length <= index) { 2418 | return (enumerator.moveNext()) 2419 | ? this.yieldReturn(cache[index] = enumerator.current()) 2420 | : false; 2421 | } 2422 | 2423 | return this.yieldReturn(cache[index]); 2424 | }, 2425 | Functions.Blank 2426 | ); 2427 | }, function () { 2428 | disposed = true; 2429 | Utils.dispose(enumerator); 2430 | cache = null; 2431 | }); 2432 | }; 2433 | 2434 | // Iterator support (ES6 for..of) 2435 | if (Utils.hasNativeIteratorSupport()) { 2436 | Enumerable.prototype[Symbol.iterator] = function () { 2437 | return { 2438 | enumerator: this.getEnumerator(), 2439 | next: function () { 2440 | if (this.enumerator.moveNext()) { 2441 | return { 2442 | done: false, 2443 | value: this.enumerator.current() 2444 | }; 2445 | } else { 2446 | return { done: true }; 2447 | } 2448 | } 2449 | }; 2450 | }; 2451 | } 2452 | 2453 | ////////////////////////// 2454 | // Error Handling Methods 2455 | 2456 | Enumerable.prototype.catchError = function (handler) { 2457 | handler = Utils.createLambda(handler); 2458 | var source = this; 2459 | 2460 | return new Enumerable(function () { 2461 | var enumerator; 2462 | 2463 | return new IEnumerator( 2464 | function () { enumerator = source.getEnumerator(); }, 2465 | function () { 2466 | try { 2467 | return (enumerator.moveNext()) 2468 | ? this.yieldReturn(enumerator.current()) 2469 | : false; 2470 | } catch (e) { 2471 | handler(e); 2472 | return false; 2473 | } 2474 | }, 2475 | function () { Utils.dispose(enumerator); }); 2476 | }); 2477 | }; 2478 | 2479 | Enumerable.prototype.finallyAction = function (finallyAction) { 2480 | finallyAction = Utils.createLambda(finallyAction); 2481 | var source = this; 2482 | 2483 | return new Enumerable(function () { 2484 | var enumerator; 2485 | 2486 | return new IEnumerator( 2487 | function () { enumerator = source.getEnumerator(); }, 2488 | function () { 2489 | return (enumerator.moveNext()) 2490 | ? this.yieldReturn(enumerator.current()) 2491 | : false; 2492 | }, 2493 | function () { 2494 | try { 2495 | Utils.dispose(enumerator); 2496 | } finally { 2497 | finallyAction(); 2498 | } 2499 | }); 2500 | }); 2501 | }; 2502 | 2503 | ///////////////// 2504 | // Debug Methods 2505 | 2506 | // Overload:function() 2507 | // Overload:function(selector) 2508 | Enumerable.prototype.log = function (selector) { 2509 | selector = Utils.createLambda(selector); 2510 | 2511 | return this.doAction(function (item) { 2512 | if (typeof console !== Types.Undefined) { 2513 | console.log(selector(item)); 2514 | } 2515 | }); 2516 | }; 2517 | 2518 | // Overload:function() 2519 | // Overload:function(message) 2520 | // Overload:function(message,selector) 2521 | Enumerable.prototype.trace = function (message, selector) { 2522 | if (message == null) message = "Trace"; 2523 | selector = Utils.createLambda(selector); 2524 | 2525 | return this.doAction(function (item) { 2526 | if (typeof console !== Types.Undefined) { 2527 | console.log(message, selector(item)); 2528 | } 2529 | }); 2530 | }; 2531 | 2532 | /////////// 2533 | // Private 2534 | 2535 | var OrderedEnumerable = function (source, keySelector, comparer, descending, parent) { 2536 | this.source = source; 2537 | this.keySelector = Utils.createLambda(keySelector); 2538 | this.descending = descending; 2539 | this.parent = parent; 2540 | 2541 | if (comparer) 2542 | this.comparer = Utils.createLambda(comparer); 2543 | }; 2544 | OrderedEnumerable.prototype = new Enumerable(); 2545 | 2546 | OrderedEnumerable.prototype.createOrderedEnumerable = function (keySelector, comparer, descending) { 2547 | return new OrderedEnumerable(this.source, keySelector, comparer, descending, this); 2548 | }; 2549 | 2550 | OrderedEnumerable.prototype.thenBy = function (keySelector, comparer) { 2551 | return this.createOrderedEnumerable(keySelector, comparer, false); 2552 | }; 2553 | 2554 | OrderedEnumerable.prototype.thenByDescending = function (keySelector, comparer) { 2555 | return this.createOrderedEnumerable(keySelector, comparer, true); 2556 | }; 2557 | 2558 | OrderedEnumerable.prototype.getEnumerator = function () { 2559 | var self = this; 2560 | var buffer; 2561 | var indexes; 2562 | var index = 0; 2563 | 2564 | return new IEnumerator( 2565 | function () { 2566 | buffer = []; 2567 | indexes = []; 2568 | self.source.forEach(function (item, index) { 2569 | buffer.push(item); 2570 | indexes.push(index); 2571 | }); 2572 | var sortContext = SortContext.create(self, null); 2573 | sortContext.GenerateKeys(buffer); 2574 | 2575 | indexes.sort(function (a, b) { return sortContext.compare(a, b); }); 2576 | }, 2577 | function () { 2578 | return (index < indexes.length) 2579 | ? this.yieldReturn(buffer[indexes[index++]]) 2580 | : false; 2581 | }, 2582 | Functions.Blank 2583 | ); 2584 | }; 2585 | 2586 | var SortContext = function (keySelector, comparer, descending, child) { 2587 | this.keySelector = keySelector; 2588 | this.descending = descending; 2589 | this.child = child; 2590 | this.comparer = comparer; 2591 | this.keys = null; 2592 | }; 2593 | 2594 | SortContext.create = function (orderedEnumerable, currentContext) { 2595 | var context = new SortContext( 2596 | orderedEnumerable.keySelector, orderedEnumerable.comparer, orderedEnumerable.descending, currentContext 2597 | ); 2598 | 2599 | if (orderedEnumerable.parent != null) return SortContext.create(orderedEnumerable.parent, context); 2600 | return context; 2601 | }; 2602 | 2603 | SortContext.prototype.GenerateKeys = function (source) { 2604 | var len = source.length; 2605 | var keySelector = this.keySelector; 2606 | var keys = new Array(len); 2607 | for (let i = 0; i < len; i++) keys[i] = keySelector(source[i]); 2608 | this.keys = keys; 2609 | 2610 | if (this.child != null) this.child.GenerateKeys(source); 2611 | }; 2612 | 2613 | SortContext.prototype.compare = function (index1, index2) { 2614 | var comparison = this.comparer ? 2615 | this.comparer(this.keys[index1], this.keys[index2]) : 2616 | Utils.compare(this.keys[index1], this.keys[index2]); 2617 | 2618 | if (comparison == 0) { 2619 | if (this.child != null) return this.child.compare(index1, index2); 2620 | return Utils.compare(index1, index2); 2621 | } 2622 | 2623 | return (this.descending) ? -comparison : comparison; 2624 | }; 2625 | 2626 | var DisposableEnumerable = function (getEnumerator, dispose) { 2627 | this.dispose = dispose; 2628 | Enumerable.call(this, getEnumerator); 2629 | }; 2630 | DisposableEnumerable.prototype = new Enumerable(); 2631 | 2632 | var ArrayEnumerable = function (source) { 2633 | this.getSource = function () { return source; }; 2634 | }; 2635 | ArrayEnumerable.prototype = new Enumerable(); 2636 | 2637 | ArrayEnumerable.prototype.any = function (predicate) { 2638 | return (predicate == null) 2639 | ? (this.getSource().length > 0) 2640 | : Enumerable.prototype.any.apply(this, arguments); 2641 | }; 2642 | 2643 | ArrayEnumerable.prototype.count = function (predicate) { 2644 | return (predicate == null) 2645 | ? this.getSource().length 2646 | : Enumerable.prototype.count.apply(this, arguments); 2647 | }; 2648 | 2649 | ArrayEnumerable.prototype.elementAt = function (index) { 2650 | var source = this.getSource(); 2651 | return (0 <= index && index < source.length) 2652 | ? source[index] 2653 | : Enumerable.prototype.elementAt.apply(this, arguments); 2654 | }; 2655 | 2656 | ArrayEnumerable.prototype.elementAtOrDefault = function (index, defaultValue) { 2657 | if (defaultValue === undefined) defaultValue = null; 2658 | var source = this.getSource(); 2659 | return (0 <= index && index < source.length) 2660 | ? source[index] 2661 | : defaultValue; 2662 | }; 2663 | 2664 | ArrayEnumerable.prototype.first = function (predicate) { 2665 | var source = this.getSource(); 2666 | return (predicate == null && source.length > 0) 2667 | ? source[0] 2668 | : Enumerable.prototype.first.apply(this, arguments); 2669 | }; 2670 | 2671 | ArrayEnumerable.prototype.firstOrDefault = function (predicate, defaultValue) { 2672 | if (predicate !== undefined) { 2673 | return Enumerable.prototype.firstOrDefault.apply(this, arguments); 2674 | } 2675 | defaultValue = predicate; 2676 | 2677 | var source = this.getSource(); 2678 | return source.length > 0 ? source[0] : defaultValue; 2679 | }; 2680 | 2681 | ArrayEnumerable.prototype.last = function (predicate) { 2682 | var source = this.getSource(); 2683 | return (predicate == null && source.length > 0) 2684 | ? source[source.length - 1] 2685 | : Enumerable.prototype.last.apply(this, arguments); 2686 | }; 2687 | 2688 | ArrayEnumerable.prototype.lastOrDefault = function (predicate, defaultValue) { 2689 | if (predicate !== undefined) { 2690 | return Enumerable.prototype.lastOrDefault.apply(this, arguments); 2691 | } 2692 | defaultValue = predicate; 2693 | 2694 | var source = this.getSource(); 2695 | return source.length > 0 ? source[source.length - 1] : defaultValue; 2696 | }; 2697 | 2698 | ArrayEnumerable.prototype.skip = function (count) { 2699 | var source = this.getSource(); 2700 | 2701 | return new Enumerable(function () { 2702 | var index; 2703 | 2704 | return new IEnumerator( 2705 | function () { index = (count < 0) ? 0 : count; }, 2706 | function () { 2707 | return (index < source.length) 2708 | ? this.yieldReturn(source[index++]) 2709 | : false; 2710 | }, 2711 | Functions.Blank); 2712 | }); 2713 | }; 2714 | 2715 | ArrayEnumerable.prototype.takeExceptLast = function (count) { 2716 | if (count == null) count = 1; 2717 | return this.take(this.getSource().length - count); 2718 | }; 2719 | 2720 | ArrayEnumerable.prototype.takeFromLast = function (count) { 2721 | return this.skip(this.getSource().length - count); 2722 | }; 2723 | 2724 | ArrayEnumerable.prototype.reverse = function () { 2725 | var source = this.getSource(); 2726 | 2727 | return new Enumerable(function () { 2728 | var index; 2729 | 2730 | return new IEnumerator( 2731 | function () { 2732 | index = source.length; 2733 | }, 2734 | function () { 2735 | return (index > 0) 2736 | ? this.yieldReturn(source[--index]) 2737 | : false; 2738 | }, 2739 | Functions.Blank); 2740 | }); 2741 | }; 2742 | 2743 | ArrayEnumerable.prototype.sequenceEqual = function (second, compareSelector) { 2744 | if ((second instanceof ArrayEnumerable || second instanceof Array) 2745 | && compareSelector == null 2746 | && Enumerable.from(second).count() != this.count()) { 2747 | return false; 2748 | } 2749 | 2750 | return Enumerable.prototype.sequenceEqual.apply(this, arguments); 2751 | }; 2752 | 2753 | ArrayEnumerable.prototype.toJoinedString = function (separator, selector) { 2754 | var source = this.getSource(); 2755 | if (selector != null || !(source instanceof Array)) { 2756 | return Enumerable.prototype.toJoinedString.apply(this, arguments); 2757 | } 2758 | 2759 | if (separator == null) separator = ""; 2760 | return source.join(separator); 2761 | }; 2762 | 2763 | ArrayEnumerable.prototype.getEnumerator = function () { 2764 | var source = this.getSource(); 2765 | var index = -1; 2766 | 2767 | // fast and simple enumerator 2768 | return { 2769 | current: function () { return source[index]; }, 2770 | moveNext: function () { 2771 | return ++index < source.length; 2772 | }, 2773 | dispose: Functions.Blank 2774 | }; 2775 | }; 2776 | 2777 | // optimization for multiple where and multiple select and whereselect 2778 | 2779 | var WhereEnumerable = function (source, predicate) { 2780 | this.prevSource = source; 2781 | this.prevPredicate = predicate; // predicate.length always <= 1 2782 | }; 2783 | WhereEnumerable.prototype = new Enumerable(); 2784 | 2785 | WhereEnumerable.prototype.where = function (predicate) { 2786 | predicate = Utils.createLambda(predicate); 2787 | 2788 | if (predicate.length <= 1) { 2789 | const prevPredicate = this.prevPredicate; 2790 | const composedPredicate = function (x) { return prevPredicate(x) && predicate(x); }; 2791 | return new WhereEnumerable(this.prevSource, composedPredicate); 2792 | } 2793 | else { 2794 | // if predicate use index, can't compose 2795 | return Enumerable.prototype.where.call(this, predicate); 2796 | } 2797 | }; 2798 | 2799 | WhereEnumerable.prototype.select = function (selector) { 2800 | selector = Utils.createLambda(selector); 2801 | 2802 | return (selector.length <= 1) 2803 | ? new WhereSelectEnumerable(this.prevSource, this.prevPredicate, selector) 2804 | : Enumerable.prototype.select.call(this, selector); 2805 | }; 2806 | 2807 | WhereEnumerable.prototype.getEnumerator = function () { 2808 | var predicate = this.prevPredicate; 2809 | var source = this.prevSource; 2810 | var enumerator; 2811 | 2812 | return new IEnumerator( 2813 | function () { enumerator = source.getEnumerator(); }, 2814 | function () { 2815 | while (enumerator.moveNext()) { 2816 | if (predicate(enumerator.current())) { 2817 | return this.yieldReturn(enumerator.current()); 2818 | } 2819 | } 2820 | return false; 2821 | }, 2822 | function () { Utils.dispose(enumerator); }); 2823 | }; 2824 | 2825 | var WhereSelectEnumerable = function (source, predicate, selector) { 2826 | this.prevSource = source; 2827 | this.prevPredicate = predicate; // predicate.length always <= 1 or null 2828 | this.prevSelector = selector; // selector.length always <= 1 2829 | }; 2830 | WhereSelectEnumerable.prototype = new Enumerable(); 2831 | 2832 | WhereSelectEnumerable.prototype.where = function (predicate) { 2833 | predicate = Utils.createLambda(predicate); 2834 | 2835 | return (predicate.length <= 1) 2836 | ? new WhereEnumerable(this, predicate) 2837 | : Enumerable.prototype.where.call(this, predicate); 2838 | }; 2839 | 2840 | WhereSelectEnumerable.prototype.select = function (selector) { 2841 | selector = Utils.createLambda(selector); 2842 | 2843 | if (selector.length <= 1) { 2844 | const prevSelector = this.prevSelector; 2845 | const composedSelector = function (x) { return selector(prevSelector(x)); }; 2846 | return new WhereSelectEnumerable(this.prevSource, this.prevPredicate, composedSelector); 2847 | } 2848 | else { 2849 | // if selector uses index, can't compose 2850 | return Enumerable.prototype.select.call(this, selector); 2851 | } 2852 | }; 2853 | 2854 | WhereSelectEnumerable.prototype.getEnumerator = function () { 2855 | var predicate = this.prevPredicate; 2856 | var selector = this.prevSelector; 2857 | var source = this.prevSource; 2858 | var enumerator; 2859 | 2860 | return new IEnumerator( 2861 | function () { enumerator = source.getEnumerator(); }, 2862 | function () { 2863 | while (enumerator.moveNext()) { 2864 | if (predicate == null || predicate(enumerator.current())) { 2865 | return this.yieldReturn(selector(enumerator.current())); 2866 | } 2867 | } 2868 | return false; 2869 | }, 2870 | function () { Utils.dispose(enumerator); }); 2871 | }; 2872 | 2873 | /////////////// 2874 | // Collections 2875 | 2876 | var Dictionary = (function () { 2877 | // static utility methods 2878 | var callHasOwnProperty = function (target, key) { 2879 | return Object.prototype.hasOwnProperty.call(target, key); 2880 | }; 2881 | 2882 | var computeHashCode = function (obj) { 2883 | if (obj === null) return "null"; 2884 | if (obj === undefined) return "undefined"; 2885 | 2886 | return (typeof obj.toString === Types.Function) 2887 | ? obj.toString() 2888 | : Object.prototype.toString.call(obj); 2889 | }; 2890 | 2891 | // LinkedList for Dictionary 2892 | var HashEntry = function (key, value) { 2893 | this.key = key; 2894 | this.value = value; 2895 | this.prev = null; 2896 | this.next = null; 2897 | }; 2898 | 2899 | var EntryList = function () { 2900 | this.first = null; 2901 | this.last = null; 2902 | }; 2903 | EntryList.prototype = 2904 | { 2905 | addLast: function (entry) { 2906 | if (this.last != null) { 2907 | this.last.next = entry; 2908 | entry.prev = this.last; 2909 | this.last = entry; 2910 | } else this.first = this.last = entry; 2911 | }, 2912 | 2913 | replace: function (entry, newEntry) { 2914 | if (entry.prev != null) { 2915 | entry.prev.next = newEntry; 2916 | newEntry.prev = entry.prev; 2917 | } else this.first = newEntry; 2918 | 2919 | if (entry.next != null) { 2920 | entry.next.prev = newEntry; 2921 | newEntry.next = entry.next; 2922 | } else this.last = newEntry; 2923 | 2924 | }, 2925 | 2926 | remove: function (entry) { 2927 | if (entry.prev != null) entry.prev.next = entry.next; 2928 | else this.first = entry.next; 2929 | 2930 | if (entry.next != null) entry.next.prev = entry.prev; 2931 | else this.last = entry.prev; 2932 | } 2933 | }; 2934 | 2935 | // Overload:function() 2936 | // Overload:function(compareSelector) 2937 | var Dictionary = function (compareSelector) { 2938 | this.countField = 0; 2939 | this.entryList = new EntryList(); 2940 | this.buckets = {}; // as Dictionary> 2941 | this.compareSelector = (compareSelector == null) ? Functions.Identity : compareSelector; 2942 | }; 2943 | Dictionary.prototype = 2944 | { 2945 | add: function (key, value) { 2946 | var compareKey = this.compareSelector(key); 2947 | var hash = computeHashCode(compareKey); 2948 | var entry = new HashEntry(key, value); 2949 | if (callHasOwnProperty(this.buckets, hash)) { 2950 | const array = this.buckets[hash]; 2951 | for (let i = 0; i < array.length; i++) { 2952 | if (this.compareSelector(array[i].key) === compareKey) { 2953 | this.entryList.replace(array[i], entry); 2954 | array[i] = entry; 2955 | return; 2956 | } 2957 | } 2958 | array.push(entry); 2959 | } else { 2960 | this.buckets[hash] = [entry]; 2961 | } 2962 | this.countField++; 2963 | this.entryList.addLast(entry); 2964 | }, 2965 | 2966 | get: function (key) { 2967 | var compareKey = this.compareSelector(key); 2968 | var hash = computeHashCode(compareKey); 2969 | if (!callHasOwnProperty(this.buckets, hash)) return undefined; 2970 | 2971 | var array = this.buckets[hash]; 2972 | for (let i = 0; i < array.length; i++) { 2973 | const entry = array[i]; 2974 | if (this.compareSelector(entry.key) === compareKey) return entry.value; 2975 | } 2976 | return undefined; 2977 | }, 2978 | 2979 | set: function (key, value) { 2980 | var compareKey = this.compareSelector(key); 2981 | var hash = computeHashCode(compareKey); 2982 | if (callHasOwnProperty(this.buckets, hash)) { 2983 | const array = this.buckets[hash]; 2984 | for (let i = 0; i < array.length; i++) { 2985 | if (this.compareSelector(array[i].key) === compareKey) { 2986 | const newEntry = new HashEntry(key, value); 2987 | this.entryList.replace(array[i], newEntry); 2988 | array[i] = newEntry; 2989 | return true; 2990 | } 2991 | } 2992 | } 2993 | return false; 2994 | }, 2995 | 2996 | contains: function (key) { 2997 | var compareKey = this.compareSelector(key); 2998 | var hash = computeHashCode(compareKey); 2999 | if (!callHasOwnProperty(this.buckets, hash)) return false; 3000 | 3001 | var array = this.buckets[hash]; 3002 | for (let i = 0; i < array.length; i++) { 3003 | if (this.compareSelector(array[i].key) === compareKey) return true; 3004 | } 3005 | return false; 3006 | }, 3007 | 3008 | clear: function () { 3009 | this.countField = 0; 3010 | this.buckets = {}; 3011 | this.entryList = new EntryList(); 3012 | }, 3013 | 3014 | remove: function (key) { 3015 | var compareKey = this.compareSelector(key); 3016 | var hash = computeHashCode(compareKey); 3017 | if (!callHasOwnProperty(this.buckets, hash)) return; 3018 | 3019 | var array = this.buckets[hash]; 3020 | for (let i = 0; i < array.length; i++) { 3021 | if (this.compareSelector(array[i].key) === compareKey) { 3022 | this.entryList.remove(array[i]); 3023 | array.splice(i, 1); 3024 | if (array.length == 0) delete this.buckets[hash]; 3025 | this.countField--; 3026 | return; 3027 | } 3028 | } 3029 | }, 3030 | 3031 | count: function () { 3032 | return this.countField; 3033 | }, 3034 | 3035 | toEnumerable: function () { 3036 | var self = this; 3037 | return new Enumerable(function () { 3038 | var currentEntry; 3039 | 3040 | return new IEnumerator( 3041 | function () { currentEntry = self.entryList.first; }, 3042 | function () { 3043 | if (currentEntry != null) { 3044 | const result = { key: currentEntry.key, value: currentEntry.value }; 3045 | currentEntry = currentEntry.next; 3046 | return this.yieldReturn(result); 3047 | } 3048 | return false; 3049 | }, 3050 | Functions.Blank); 3051 | }); 3052 | } 3053 | }; 3054 | 3055 | return Dictionary; 3056 | })(); 3057 | 3058 | // dictionary = Dictionary 3059 | var Lookup = function (dictionary) { 3060 | this.count = function () { 3061 | return dictionary.count(); 3062 | }; 3063 | this.get = function (key) { 3064 | return Enumerable.from(dictionary.get(key)); 3065 | }; 3066 | this.contains = function (key) { 3067 | return dictionary.contains(key); 3068 | }; 3069 | this.toEnumerable = function () { 3070 | return dictionary.toEnumerable().select(function (kvp) { 3071 | return new Grouping(kvp.key, kvp.value); 3072 | }); 3073 | }; 3074 | }; 3075 | 3076 | var Grouping = function (groupKey, elements) { 3077 | this.key = function () { 3078 | return groupKey; 3079 | }; 3080 | ArrayEnumerable.call(this, elements); 3081 | }; 3082 | Grouping.prototype = new ArrayEnumerable(); 3083 | 3084 | export default Enumerable; 3085 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "linq", 3 | "author": "Mihai Ciuraru ", 4 | "description": "linq.js - LINQ for JavaScript", 5 | "type": "module", 6 | "version": "4.0.3", 7 | "license": "MIT", 8 | "homepage": "https://github.com/mihaifm/linq", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/mihaifm/linq.git" 12 | }, 13 | "scripts": { 14 | "test": "node test/testrunner.js" 15 | }, 16 | "preferGlobal": false, 17 | "keywords": [ 18 | "linq" 19 | ], 20 | "dependencies": {}, 21 | "engines": { 22 | "node": "*" 23 | }, 24 | "main": "./linq.js", 25 | "exports": "./linq.js", 26 | "types": "./linq.d.ts" 27 | } 28 | -------------------------------------------------------------------------------- /sample/tutorial.js: -------------------------------------------------------------------------------- 1 | import Enumerable from '../linq.js' 2 | 3 | ///////////////////////////// 4 | // Simple Lambda Expressions 5 | 6 | console.log('# Simple Lambda Expressions\n'); 7 | 8 | // Anonymous function 9 | Enumerable.range(1, 3).select(function(value, index) { return index + ':' + value }).log().toJoinedString(); 10 | 11 | // Arrow function syntax 12 | Enumerable.range(1, 3).select((value, index) => index + ':' + value).log().toJoinedString(); 13 | 14 | // Lambdas can also be passed as strings 15 | Enumerable.range(1, 3).select("(value, index) => index + '/' + value").log().toJoinedString(); 16 | 17 | // If the number of arguments is one, we can use default iterator variable '$' 18 | Enumerable.range(1, 3).select("$*2").log().toJoinedString(); // same as i => i * 2 19 | 20 | // "" is a shortcut for "x => x" (identity function) 21 | Enumerable.range(4, 7).join(Enumerable.range(8, 5), "", "", (outer, inner) => outer * inner).log().toJoinedString(); 22 | 23 | 24 | /////////////////////////////// 25 | // Scope of lambda expressions 26 | 27 | console.log('\n# Scope of lambda expressions\n'); 28 | 29 | // lambda expressions cannot access the closure when the string syntax is used 30 | try { 31 | var number = 3; 32 | Enumerable.range(1,10).where("$ == number").log().toJoinedString(); 33 | } catch (e) { 34 | console.log("can't find number"); 35 | } 36 | 37 | // use anonymous function to capture variable 38 | Enumerable.range(1,10).where(function(i){return i == number}).log().toJoinedString(); 39 | 40 | 41 | ///////////////////////////// 42 | // Initializing from objects 43 | 44 | console.log('\n# Initializing from objects\n'); 45 | 46 | var object = {foo:"a", "bar":100, "foobar":true}; 47 | Enumerable.from(object).forEach(function(obj) { console.log(obj.key + ":" + obj.value) }); 48 | 49 | 50 | ///////////////////////////////////// 51 | // Continue and break when iterating 52 | 53 | console.log('\n# Continue and break when iterating\n'); 54 | 55 | Enumerable.repeat("foo", 10).forEach(function(value, index) 56 | { 57 | if (index % 2 == 0) return; // continue 58 | if (index > 6) return false; // break 59 | console.log(index + ":" + value); 60 | }); 61 | 62 | 63 | ////////////////////////////////// 64 | // Grouping and ref/value compare 65 | 66 | console.log('\n# Grouping and ref/value compare\n'); 67 | 68 | // ref compare 69 | console.log((new Date(2000, 1, 1) == new Date(2000, 1, 1))); // false 70 | console.log(({ a: 0} == { a: 0 })); // false 71 | 72 | console.log("------"); 73 | var objects = [ 74 | { Date: new Date(2000, 1, 1), Id: 1 }, 75 | { Date: new Date(2010, 5, 5), Id: 2 }, 76 | { Date: new Date(2000, 1, 1), Id: 3 } 77 | ] 78 | 79 | // ref compare, cannot do grouping 80 | Enumerable.from(objects) 81 | .groupBy("$.Date", "$.Id", 82 | function (key, group) { return { date: key, ids: group.toJoinedString(',')} }) 83 | .log("$.date + ':' + $.ids").toJoinedString(); 84 | 85 | console.log("------"); 86 | 87 | // use fourth argument to groupBy (compareSelector) 88 | Enumerable.from(objects) 89 | .groupBy("$.Date", "$.Id", 90 | function (key, group) { return { date: key, ids: group.toJoinedString(',')} }, 91 | function (key) { return key.toString() }) 92 | .log("$.date + ':' + $.ids").toJoinedString(); 93 | 94 | 95 | ////////////////////////////// 96 | // Regular Expression matches 97 | 98 | console.log('\n# Regular Expression matches\n'); 99 | 100 | // Enumerable.matches return Enumerable 101 | 102 | var input = "abcdefgABzDefabgdg"; 103 | Enumerable.matches(input, "ab(.)d", "i").forEach(function(match) 104 | { 105 | for (var prop in match) 106 | { 107 | console.log(prop + " : " + match[prop]); 108 | } 109 | console.log("toString() : " + match.toString()); 110 | console.log("---"); 111 | }); 112 | 113 | 114 | /////////////////////////////////// 115 | // LazyEvaluation and InfinityList 116 | 117 | console.log('\n# LazyEvaluation and InfinityList\n'); 118 | 119 | // first radius of a circle with area over 10000 120 | var result = Enumerable.toInfinity(1).where(r => r * r * Math.PI > 10000).first(); 121 | console.log(result); 122 | 123 | 124 | ////////////// 125 | // Dictionary 126 | 127 | console.log('\n# Dictionary\n'); 128 | 129 | // sample class 130 | var cls = function (a, b) 131 | { 132 | this.a = a; 133 | this.b = b; 134 | } 135 | var instanceA = new cls("a", 100); 136 | var instanceB = new cls("b", 2000); 137 | 138 | // create blank dictionary 139 | var dict = Enumerable.empty().toDictionary(); 140 | // create blank dictionary (using compareSelector) 141 | var dict = Enumerable.empty().toDictionary("","",function (x) { return x.a + x.b }); 142 | 143 | dict.add(instanceA, "zzz"); 144 | dict.add(instanceB, "huga"); 145 | console.log(dict.get(instanceA)); // zzz 146 | console.log(dict.get(instanceB)); // huga 147 | 148 | // enumerable (to keyvaluePair) 149 | dict.toEnumerable().forEach(function (kvp) 150 | { 151 | console.log(kvp.key.a + ":" + kvp.value); 152 | }); 153 | 154 | 155 | ///////////////////////////// 156 | // Nondeterministic Programs 157 | 158 | console.log('\n# Nondeterministic programs\n'); 159 | 160 | // a puzzle from Structure and Interpretation of Computer Programs 161 | // http://sarabander.github.io/sicp/html/4_002e3.xhtml 162 | 163 | var apart = Enumerable.range(1, 5); 164 | var answers = apart 165 | .selectMany(function(baker){ return apart 166 | .selectMany(function(cooper){ return apart 167 | .selectMany(function(fletcher){ return apart 168 | .selectMany(function(miller){ return apart 169 | .select(function(smith){ return { 170 | baker: baker, cooper: cooper, fletcher: fletcher, miller: miller, smith: smith}})})})})}) 171 | .where(function(x){ return Enumerable.from(x).distinct("$.value").count() == 5 }) 172 | .where("$.baker != 5") 173 | .where("$.cooper != 1") 174 | .where("$.fletcher != 1 && $.fletcher != 5") 175 | .where("$.miller > $.cooper") 176 | .where("Math.abs($.smith - $.fletcher) != 1") 177 | .where("Math.abs($.fletcher - $.cooper) != 1"); 178 | 179 | answers.selectMany("").log("$.key + ':' + $.value").toJoinedString(); 180 | -------------------------------------------------------------------------------- /test/action.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Action"); 5 | 6 | test("doAction", function () 7 | { 8 | var array = []; 9 | let actual = Enumerable.range(1, 10).doAction(function (i) { array.push(i) }).toArray(); 10 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 11 | 12 | array = [] 13 | var array2 = [] 14 | actual = Enumerable.range(1, 10).doAction(function (v, i) { array.push(v); array2.push(i); }).toArray(); 15 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 16 | deepEqual(actual, array); 17 | deepEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], array2); 18 | }); 19 | 20 | test("forEach", function () 21 | { 22 | let actual = []; 23 | Enumerable.range(1, 10).forEach(function (i) { actual.push(i) }); 24 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 25 | 26 | actual = []; 27 | var actual2 = []; 28 | Enumerable.range(1, 10).forEach(function (v, i) { actual.push(v); actual2.push(i); }); 29 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 30 | deepEqual(actual2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 31 | 32 | actual = []; 33 | Enumerable.range(1, 10).forEach(function (i) { if (i == 5) return false; actual.push(i); }); 34 | deepEqual(actual, [1, 2, 3, 4]); 35 | 36 | actual = []; 37 | actual2 = []; 38 | Enumerable.range(1, 10).forEach(function (v, i) { if (i == 5) return false; actual.push(v); actual2.push(i); }); 39 | deepEqual(actual, [1, 2, 3, 4, 5]); 40 | deepEqual(actual2, [0, 1, 2, 3, 4]); 41 | }); 42 | 43 | test("force", function () 44 | { 45 | let actual = []; 46 | Enumerable.range(1, 10).doAction(function (i) { actual.push(i) }).force(); 47 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 48 | }); 49 | -------------------------------------------------------------------------------- /test/aggregate.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, strictEqual, equal } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Aggregate"); 5 | 6 | test("aggregate", function () { 7 | strictEqual(Enumerable.range(1, 10).aggregate("a,b=>a+b"), 55); 8 | strictEqual(Enumerable.range(1, 10).aggregate(10, "a,b=>a+b"), 65); 9 | strictEqual(Enumerable.range(1, 10).aggregate(10, "a,b=>a+b", "val=>val*10"), 650); 10 | strictEqual(Enumerable.range(1, 10).aggregate("", "s,x=>s+x", "'hoge' + $"), "hoge12345678910"); 11 | }); 12 | 13 | test("average", function () { 14 | strictEqual(Enumerable.range(1, 10).average(), 5.5); 15 | strictEqual(Enumerable.range(1, 10).select("v,i=>{v:v,i:i}").average("t=>t.i"), 4.5); 16 | }); 17 | 18 | test("count", function () { 19 | let actual = Enumerable.range(1, 10).count(); 20 | equal(actual, 10); 21 | actual = Enumerable.empty().count(); 22 | equal(actual, 0); 23 | 24 | actual = Enumerable.range(1, 10).count("i=>i<5"); 25 | equal(actual, 4); 26 | }); 27 | 28 | test("Max", function () { 29 | let actual = Enumerable.range(1, 10).max(); 30 | equal(actual, 10); 31 | 32 | actual = Enumerable.range(1, 10).select("v,i=>{v:v,i:i}").max("t=>t.i"); 33 | equal(actual, 9); 34 | }); 35 | 36 | test("min", function () { 37 | let actual = Enumerable.range(1, 10).min(); 38 | equal(actual, 1); 39 | 40 | actual = Enumerable.range(1, 10).select("v,i=>{v:v,i:i}").min("t=>t.i"); 41 | equal(actual, 0); 42 | }); 43 | 44 | test("maxBy", function () { 45 | let actual = Enumerable.range(1, 10).select("v,i=>{v:v,i:i}").maxBy("t=>t.i"); 46 | deepEqual(actual, { v: 10, i: 9 }); 47 | }); 48 | 49 | test("minBy", function () { 50 | let actual = Enumerable.range(1, 10).select("v,i=>{v:v,i:i}").minBy("t=>t.i"); 51 | deepEqual(actual, { v: 1, i: 0 }); 52 | }); 53 | 54 | test("sum", function () { 55 | let actual = Enumerable.range(1, 10).sum(); 56 | equal(actual, 55); 57 | actual = Enumerable.empty().sum(); 58 | equal(actual, 0); 59 | 60 | actual = Enumerable.range(1, 10).select("v,i=>{v:v,i:i}").sum("t=>t.i"); 61 | equal(actual, 45); 62 | }); 63 | -------------------------------------------------------------------------------- /test/arrayEnumerable.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, strictEqual, equal, ok } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("ArrayEnumerable"); 5 | 6 | var arraySequence = Enumerable.from([1, 10, 100, 1000, 10000]); 7 | var emptySequence = Enumerable.from([]); 8 | 9 | test("any", function () { 10 | ok(arraySequence.any()); 11 | ok(!emptySequence.any()); 12 | ok(arraySequence.any("$==100")); 13 | ok(!emptySequence.any("$==2")); 14 | }); 15 | 16 | test("count", function () { 17 | equal(arraySequence.count(), 5); 18 | equal(emptySequence.count(), 0); 19 | equal(arraySequence.count("$<=100"), 3); 20 | equal(emptySequence.count("$<=100"), 0); 21 | }); 22 | 23 | test("elementAt", function () { 24 | equal(arraySequence.elementAt(3), 1000); 25 | try { 26 | arraySequence.elementAt(-1); 27 | ok(false); 28 | } 29 | catch (e) { ok(true, "okay"); } 30 | 31 | try { 32 | arraySequence.elementAt(100); 33 | ok(false); 34 | } 35 | catch (e) { ok(true); } 36 | }); 37 | 38 | test("elementAtOrDefault", function () { 39 | equal(arraySequence.elementAtOrDefault(4), 10000); 40 | equal(arraySequence.elementAtOrDefault(-1, -100), -100); 41 | equal(arraySequence.elementAtOrDefault(5, -100), -100); 42 | }); 43 | 44 | test("first", function () { 45 | equal(arraySequence.first(), 1); 46 | equal(arraySequence.first("$>=100"), 100); 47 | 48 | try { 49 | arraySequence.first("$==-1"); 50 | ok(false); 51 | } 52 | catch (e) { ok(true); } 53 | 54 | try { 55 | emptySequence.first(); 56 | ok(false); 57 | } 58 | catch (e) { ok(true); } 59 | }); 60 | 61 | test("firstOrDefault", function () { 62 | // No arguments. 63 | strictEqual(arraySequence.firstOrDefault(), 1); 64 | strictEqual(emptySequence.firstOrDefault(), undefined); 65 | 66 | // No predicate. 67 | strictEqual(arraySequence.firstOrDefault(0), 1); 68 | strictEqual(emptySequence.firstOrDefault(0), 0); 69 | strictEqual(emptySequence.firstOrDefault(undefined), undefined); 70 | 71 | // "null" predicate. 72 | strictEqual(arraySequence.firstOrDefault(null, 0), 1); 73 | strictEqual(emptySequence.firstOrDefault(null), undefined); // Because "null" is treated as noop predicate. 74 | strictEqual(emptySequence.firstOrDefault(null, 0), 0); 75 | strictEqual(emptySequence.firstOrDefault(null, null), null); 76 | strictEqual(emptySequence.firstOrDefault(null, undefined), undefined); 77 | 78 | // No default value. 79 | strictEqual(arraySequence.firstOrDefault("i=>true"), 1); 80 | strictEqual(emptySequence.firstOrDefault("i=>true"), undefined); 81 | 82 | // Both arguments. 83 | strictEqual(arraySequence.firstOrDefault("i=>true", 0), 1); 84 | strictEqual(emptySequence.firstOrDefault("i=>true", 0), 0); 85 | strictEqual(emptySequence.firstOrDefault("i=>true", null), null); 86 | strictEqual(emptySequence.firstOrDefault("i=>true", undefined), undefined); 87 | }); 88 | 89 | test("last", function () { 90 | equal(arraySequence.last(), 10000); 91 | equal(arraySequence.last("$<=500"), 100); 92 | 93 | try { 94 | arraySequence.last("$==-1"); 95 | ok(false); 96 | } 97 | catch (e) { ok(true); } 98 | 99 | try { 100 | emptySequence.last(); 101 | ok(false); 102 | } 103 | catch (e) { ok(true); } 104 | }); 105 | 106 | test("lastOrDefault", function () { 107 | // No arguments. 108 | strictEqual(arraySequence.lastOrDefault(), 10000); 109 | strictEqual(emptySequence.lastOrDefault(), undefined); 110 | 111 | // No predicate. 112 | strictEqual(arraySequence.lastOrDefault(0), 10000); 113 | strictEqual(emptySequence.lastOrDefault(0), 0); 114 | strictEqual(emptySequence.lastOrDefault(undefined), undefined); 115 | 116 | // "null" predicate. 117 | strictEqual(arraySequence.lastOrDefault(null, 0), 10000); 118 | strictEqual(emptySequence.lastOrDefault(null), undefined); // Because "null" is treated as noop predicate. 119 | strictEqual(emptySequence.lastOrDefault(null, 0), 0); 120 | strictEqual(emptySequence.lastOrDefault(null, null), null); 121 | strictEqual(emptySequence.lastOrDefault(null, undefined), undefined); 122 | 123 | // No default value. 124 | strictEqual(arraySequence.lastOrDefault("i=>true"), 10000); 125 | strictEqual(emptySequence.lastOrDefault("i=>true"), undefined); 126 | 127 | // Both arguments. 128 | strictEqual(arraySequence.lastOrDefault("i=>true", 0), 10000); 129 | strictEqual(emptySequence.lastOrDefault("i=>true", 0), 0); 130 | strictEqual(emptySequence.lastOrDefault("i=>true", null), null); 131 | strictEqual(emptySequence.lastOrDefault("i=>true", undefined), undefined); 132 | }); 133 | 134 | test("skip", function () { 135 | deepEqual(arraySequence.skip(3).toArray(), [1000, 10000]); 136 | deepEqual(arraySequence.skip(-10).toArray(), [1, 10, 100, 1000, 10000]); 137 | deepEqual(arraySequence.skip(10).toArray(), []); 138 | deepEqual(emptySequence.skip(3).toArray(), []); 139 | }); 140 | 141 | test("takeExceptLast", function () { 142 | deepEqual(arraySequence.takeExceptLast().toArray(), [1, 10, 100, 1000]); 143 | deepEqual(arraySequence.takeExceptLast(3).toArray(), [1, 10]); 144 | deepEqual(arraySequence.takeExceptLast(-100).toArray(), [1, 10, 100, 1000, 10000]); 145 | deepEqual(arraySequence.takeExceptLast(0).toArray(), [1, 10, 100, 1000, 10000]); 146 | deepEqual(arraySequence.takeExceptLast(100).toArray(), []); 147 | }); 148 | 149 | test("takeFromLast", function () { 150 | deepEqual(arraySequence.takeFromLast(3).toArray(), [100, 1000, 10000]); 151 | deepEqual(arraySequence.takeFromLast(0).toArray(), []); 152 | deepEqual(arraySequence.takeFromLast(-100).toArray(), []); 153 | deepEqual(arraySequence.takeFromLast(100).toArray(), [1, 10, 100, 1000, 10000]); 154 | }); 155 | 156 | test("reverse", function () { 157 | deepEqual(arraySequence.reverse().toArray(), [10000, 1000, 100, 10, 1]); 158 | deepEqual(emptySequence.reverse().toArray(), []); 159 | }); 160 | 161 | test("sequenceEqual", function () { 162 | ok(arraySequence.sequenceEqual([1, 10, 100, 1000, 10000])); 163 | ok(!arraySequence.sequenceEqual([1, 10, 100, 1000, 10000, 100000])); 164 | ok(!arraySequence.sequenceEqual([1, 10, 100, 1000])); 165 | ok(!arraySequence.sequenceEqual([1, 10, 500, 1000, 10000])); 166 | }); 167 | 168 | test("toJoinedString", function () { 169 | equal(arraySequence.toJoinedString(), "110100100010000"); 170 | equal(arraySequence.toJoinedString("-"), "1-10-100-1000-10000"); 171 | equal(arraySequence.toJoinedString("-", "$+1"), "2-11-101-1001-10001"); 172 | }); 173 | -------------------------------------------------------------------------------- /test/convert.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, equal, ok } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Convert"); 5 | 6 | test("toArray", function () 7 | { 8 | let actual = Enumerable.range(1, 10).toArray(); 9 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 10 | }); 11 | 12 | test("toLookup", function () 13 | { 14 | var fileList = ["temp.xls", "temp2.xls", "temp.pdf", "temp.jpg", "temp2.pdf"]; 15 | let actual = Enumerable.from(fileList).toLookup("file=>file.match(/\\.(.+$)/)[1]"); 16 | 17 | deepEqual(["temp.xls", "temp2.xls"], actual.get("xls").toArray()); 18 | deepEqual(["temp.pdf", "temp2.pdf"], actual.get("pdf").toArray()); 19 | deepEqual(["temp.jpg"], actual.get("jpg").toArray()); 20 | equal(3, actual.count()); 21 | ok(actual.contains("xls")); 22 | ok(!actual.contains("XLS")); 23 | var array = actual.toEnumerable().toArray(); 24 | equal("xls", array[0].key()); 25 | deepEqual(["temp.xls", "temp2.xls"], array[0].toArray()); 26 | 27 | actual = Enumerable.from(fileList).toLookup("file=>file.match(/\\.(.+$)/)[1]", "file=>file +'ele'"); 28 | deepEqual(["temp.xlsele", "temp2.xlsele"], actual.get("xls").toArray()); 29 | deepEqual(["temp.pdfele", "temp2.pdfele"], actual.get("pdf").toArray()); 30 | deepEqual(["temp.jpgele"], actual.get("jpg").toArray()); 31 | 32 | fileList = ["temp.xls", "temp2.XLS", "temp.pdf", "temp.jpg", "temp2.pDf"]; 33 | actual = Enumerable.from(fileList).toLookup("file=>file.match(/\\.(.+$)/)[1]", "file=>file +'ele'", 34 | function (s) { return s.toLowerCase() }); 35 | deepEqual(actual.get("xLS").toArray(), ["temp.xlsele", "temp2.XLSele"]); 36 | deepEqual(actual.get("PDf").toArray(), ["temp.pdfele", "temp2.pDfele"]); 37 | deepEqual(actual.get("Jpg").toArray(), ["temp.jpgele"]); 38 | ok(actual.contains("xls")); 39 | ok(actual.contains("XLS")); 40 | }); 41 | 42 | test("toObject", function () 43 | { 44 | let actual = Enumerable.range(1, 3).toObject("i=>'foo'+i", "i=>i*4"); 45 | deepEqual(actual, { foo1: 4, foo2: 8, foo3: 12 }); 46 | }); 47 | 48 | 49 | test("toDictionary", function () 50 | { 51 | let actual = Enumerable.range(1, 3).toDictionary("i=>'foo'+i", "i=>i*4"); 52 | equal(4, actual.get("foo1")); 53 | equal(8, actual.get("foo2")); 54 | equal(12, actual.get("foo3")); 55 | 56 | actual = Enumerable.range(1, 3).toDictionary("i=>{key:i,V:'foo'+i}", "i=>i*4", "$.key"); 57 | equal(4, actual.get({ key: 1 })); 58 | equal(8, actual.get({ key: 2 })); 59 | equal(12, actual.get({ key: 3 })); 60 | }); 61 | 62 | test("toJoinedString", function () 63 | { 64 | let actual = Enumerable.range(1, 3).toJoinedString(); 65 | equal(actual, "123"); 66 | 67 | actual = Enumerable.range(1, 3).toJoinedString("-"); 68 | equal(actual, "1-2-3"); 69 | 70 | actual = Enumerable.range(1, 3).toJoinedString("-", "i=>i*2"); 71 | equal(actual, "2-4-6"); 72 | }); 73 | 74 | test("toJSONString", function () 75 | { 76 | let actual = Enumerable.from([{ a: 1, b: true }, { a: null, b: "aaa"}]).toJSONString(); 77 | equal(actual, '[{"a":1,"b":true},{"a":null,"b":"aaa"}]'); 78 | 79 | actual = Enumerable.range(1, 5).toJSONString(); 80 | equal(actual, '[1,2,3,4,5]'); 81 | 82 | actual = Enumerable.from(["a", "b", "c"]) 83 | .toJSONString(function (key, value) 84 | { 85 | if (typeof value === 'object') return value; 86 | return value.toString().toUpperCase(); 87 | }); 88 | equal(actual, '["A","B","C"]'); 89 | 90 | actual = Enumerable.from([1, 2, 3, 4, 5]) 91 | .toJSONString(function (key, value) { return value; }, 1); 92 | ok(actual.indexOf("\n") != -1); 93 | }); 94 | -------------------------------------------------------------------------------- /test/dictionary.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, equal, ok } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Dictionary"); 5 | 6 | var aComparer = function (x) { return x.a } 7 | var obj1 = { a: 1 } 8 | var obj1_ = { a: 1 } 9 | var obj2 = { a: 2 } 10 | var obj2_ = { a: 2 } 11 | 12 | test("AddGetCountRemoveClear", function () 13 | { 14 | var dict = Enumerable.empty().toDictionary(); 15 | dict.add("a", 1); 16 | dict.add("b", 2); 17 | dict.add("c", 3); 18 | dict.add("c", 100); 19 | equal(1, dict.get("a")); 20 | equal(2, dict.get("b")); 21 | equal(100, dict.get("c")); 22 | 23 | dict.add(obj1, 1); 24 | dict.add(obj1_, 2); 25 | dict.add(obj2, 3); 26 | dict.add(obj2_, 4); 27 | equal(1, dict.get(obj1)); 28 | equal(2, dict.get(obj1_)); 29 | equal(3, dict.get(obj2)); 30 | equal(4, dict.get(obj2_)); 31 | 32 | equal(7, dict.count()); 33 | 34 | dict.remove("a"); 35 | dict.remove(obj1); 36 | dict.remove(obj1_); 37 | dict.remove(obj2_); 38 | equal(undefined, dict.get("a")); 39 | equal(undefined, dict.get(obj1)); 40 | equal(undefined, dict.get(obj1_)); 41 | equal(undefined, dict.get(obj2_)); 42 | 43 | equal(3, dict.count()); 44 | dict.clear(); 45 | equal(undefined, dict.get("b")); 46 | equal(undefined, dict.get(obj2)); 47 | equal(0, dict.count()); 48 | 49 | dict = Enumerable.empty().toDictionary("", "", aComparer); 50 | 51 | dict.add(obj1, 1); 52 | dict.add(obj1_, 2); 53 | dict.add(obj2, 3); 54 | dict.add(obj2_, 4); 55 | equal(2, dict.get(obj1)); 56 | equal(2, dict.get(obj1_)); 57 | equal(4, dict.get(obj2)); 58 | equal(4, dict.get(obj2_)); 59 | 60 | equal(2, dict.count()); 61 | 62 | dict.remove(obj1); 63 | equal(undefined, dict.get(obj1)); 64 | equal(undefined, dict.get(obj1_)); 65 | 66 | equal(1, dict.count()); 67 | dict.clear(); 68 | equal(undefined, dict.get(obj2)); 69 | equal(undefined, dict.get(obj2_)); 70 | equal(0, dict.count()); 71 | }); 72 | 73 | test("SetContains", function () 74 | { 75 | var dict = Enumerable.empty().toDictionary(); 76 | dict.add("a", 1); 77 | dict.add("b", 2); 78 | dict.add(obj1, 1); 79 | dict.add(obj1_, 2); 80 | dict.set("a", 1000); 81 | dict.set("b", 2000); 82 | dict.set(obj1, 10000); 83 | dict.set(obj1_, 20000); 84 | equal(1000, dict.get("a")); 85 | equal(2000, dict.get("b")); 86 | equal(10000, dict.get(obj1)); 87 | equal(20000, dict.get(obj1_)); 88 | ok(dict.contains("a")); 89 | ok(dict.contains("b")); 90 | ok(dict.contains(obj1)); 91 | ok(dict.contains(obj1_)); 92 | ok(!dict.contains("c")); 93 | ok(!dict.contains(obj2)); 94 | 95 | dict = Enumerable.empty().toDictionary("", "", aComparer); 96 | dict.add(obj1, 1); 97 | dict.add(obj1_, 2); 98 | dict.add(obj2, 3); 99 | dict.add(obj2_, 4); 100 | dict.set(obj1, 10000); 101 | dict.set(obj1_, 20000); 102 | dict.set(obj2, 30000); 103 | dict.set(obj2_, 40000); 104 | equal(20000, dict.get(obj1)); 105 | equal(20000, dict.get(obj1_)); 106 | equal(40000, dict.get(obj2)); 107 | equal(40000, dict.get(obj2_)); 108 | ok(dict.contains(obj1)); 109 | ok(dict.contains(obj1_)); 110 | ok(!dict.contains({ a: 3 })); 111 | }); 112 | 113 | test("toEnumerable", function () 114 | { 115 | var dict = Enumerable.empty().toDictionary(); 116 | dict.add("a", 1); 117 | dict.add("b", 2); 118 | dict.add("c", 3); 119 | 120 | var ar = dict.toEnumerable().orderBy("$.key").toArray(); 121 | equal("a", ar[0].key); 122 | equal(1, ar[0].value); 123 | equal("b", ar[1].key); 124 | equal(2, ar[1].value); 125 | equal("c", ar[2].key); 126 | equal(3, ar[2].value); 127 | 128 | dict.clear(); 129 | dict.add(obj1, 1); 130 | dict.add(obj1_, 2); 131 | dict.add(obj2, 3); 132 | dict.add(obj2_, 4); 133 | 134 | ar = dict.toEnumerable().orderBy("$.key.a").toArray(); 135 | equal(obj1, ar[0].key); 136 | equal(1, ar[0].value); 137 | equal(obj1_, ar[1].key); 138 | equal(2, ar[1].value); 139 | equal(obj2, ar[2].key); 140 | equal(3, ar[2].value); 141 | equal(obj2_, ar[3].key); 142 | equal(4, ar[3].value); 143 | 144 | dict = Enumerable.empty().toDictionary("", "", aComparer); 145 | dict.add(obj1, 1); 146 | dict.add(obj1_, 2); 147 | dict.add(obj2, 3); 148 | dict.add(obj2_, 4); 149 | ar = dict.toEnumerable().orderBy("$.key.a").toArray(); 150 | equal(obj1_, ar[0].key); 151 | equal(2, ar[0].value); 152 | equal(obj2_, ar[1].key); 153 | equal(4, ar[1].value); 154 | }); 155 | -------------------------------------------------------------------------------- /test/enumerable.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, equal, notEqual, strictNotEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Enumerable"); 5 | 6 | test("choice", function () { 7 | let actual = Enumerable.choice(1, 10, 31, 42).take(10).toArray(); 8 | notEqual(actual, [1, 10, 31, 42, 1, 10, 31, 42, 1, 10], "random test. if failed retry"); 9 | equal(actual.length, 10); 10 | 11 | actual = Enumerable.choice([1, 10, 31, 42]).take(10).toArray(); 12 | notEqual(actual, [1, 10, 31, 42, 1, 10, 31, 42, 1, 10], "random test. if failed retry"); 13 | equal(actual.length, 10); 14 | 15 | var seq = Enumerable.make(1).concat([10]).concat([31]).concat([42]); 16 | 17 | actual = Enumerable.choice(seq).take(10).toArray(); 18 | notEqual(actual, [1, 10, 31, 42, 1, 10, 31, 42, 1, 10], "random test. if failed retry"); 19 | equal(actual.length, 10); 20 | }); 21 | 22 | test("cycle", function () { 23 | let actual = Enumerable.cycle(1, 10, 31, 42).take(10).toArray(); 24 | deepEqual(actual, [1, 10, 31, 42, 1, 10, 31, 42, 1, 10]); 25 | actual = Enumerable.cycle([1, 2, 3, 4, 5]).take(10).toArray(); 26 | deepEqual(actual, [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]); 27 | 28 | var seq = Enumerable.make(1).concat([10]).concat([31]).concat([42]); 29 | actual = Enumerable.cycle(seq).take(10).toArray(); 30 | deepEqual(actual, [1, 10, 31, 42, 1, 10, 31, 42, 1, 10]); 31 | 32 | actual = Enumerable.cycle(Enumerable.range(1, 5)).take(10).toArray(); 33 | deepEqual(actual, [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]); 34 | 35 | deepEqual(Enumerable.cycle(1, 2, 3).take(5).toArray(), [1, 2, 3, 1, 2]); 36 | }); 37 | 38 | test("empty", function () { 39 | let actual = Enumerable.empty().toArray(); 40 | deepEqual(actual, []); 41 | }); 42 | 43 | test("from", function () { 44 | let actual = Enumerable.from("temp").toArray(); 45 | deepEqual(actual, ["t", "e", "m", "p"]); 46 | 47 | actual = Enumerable.from(3).toArray(); 48 | deepEqual(actual, [3]); 49 | 50 | actual = Enumerable.from([1, 2, 3, 4, 5]).toArray(); 51 | deepEqual(actual, [1, 2, 3, 4, 5]); 52 | 53 | actual = Enumerable.from({ foo: "bar", func: function () { } }).toArray(); 54 | deepEqual(actual, [{ key: "foo", value: "bar" }]); 55 | }); 56 | 57 | test("make", function () { 58 | let actual = Enumerable.make("hoge").toArray(); 59 | deepEqual(actual, ["hoge"]); 60 | }); 61 | 62 | test("matches", function () { 63 | let actual = Enumerable.matches("xbcyBCzbc", /(.)bc/i).select("$.index+$[1]").toArray(); 64 | deepEqual(actual, ["0x", "3y", "6z"]); 65 | actual = Enumerable.matches("xbcyBCzbc", "(.)bc").select("$.index+$[1]").toArray();; 66 | deepEqual(actual, ["0x", "6z"]); 67 | actual = Enumerable.matches("xbcyBCzbc", "(.)bc", "i").select("$.index+$[1]").toArray();; 68 | deepEqual(actual, ["0x", "3y", "6z"]); 69 | }); 70 | 71 | test("range", function () { 72 | let actual = Enumerable.range(1, 10).toArray(); 73 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 74 | actual = Enumerable.range(1, 5, 3).toArray(); 75 | deepEqual(actual, [1, 4, 7, 10, 13]); 76 | 77 | deepEqual(Enumerable.range(3, 4).toArray(), [3, 4, 5, 6]); 78 | deepEqual(Enumerable.range(-2, 4).toArray(), [-2, -1, 0, 1]); 79 | deepEqual(Enumerable.range(-2, 4, 2).toArray(), [-2, 0, 2, 4]); 80 | }); 81 | 82 | test("rangeDown", function () { 83 | let actual = Enumerable.rangeDown(1, 10).toArray(); 84 | deepEqual(actual, [1, 0, -1, -2, -3, -4, -5, -6, -7, -8]); 85 | actual = Enumerable.rangeDown(1, 5, 3).toArray(); 86 | deepEqual(actual, [1, -2, -5, -8, -11]); 87 | 88 | deepEqual(Enumerable.rangeDown(3, 5).toArray(), [3, 2, 1, 0, -1]); 89 | deepEqual(Enumerable.rangeDown(-2, 4).toArray(), [-2, -3, -4, -5]); 90 | deepEqual(Enumerable.rangeDown(-2, 4, 2).toArray(), [-2, -4, -6, -8]); 91 | }); 92 | 93 | test("rangeTo", function () { 94 | let actual = Enumerable.rangeTo(5, 10).toArray(); 95 | deepEqual(actual, [5, 6, 7, 8, 9, 10]); 96 | actual = Enumerable.rangeTo(1, 10, 3).toArray(); 97 | deepEqual(actual, [1, 4, 7, 10]); 98 | actual = Enumerable.rangeTo(-2, -8).toArray(); 99 | deepEqual(actual, [-2, -3, -4, -5, -6, -7, -8]); 100 | actual = Enumerable.rangeTo(-2, -8, 2).toArray(); 101 | deepEqual(actual, [-2, -4, -6, -8]); 102 | 103 | deepEqual(Enumerable.rangeTo(1, 4).toArray(), [1, 2, 3, 4]); 104 | deepEqual(Enumerable.rangeTo(-3, 6).toArray(), [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]); 105 | deepEqual(Enumerable.rangeTo(2, -5).toArray(), [2, 1, 0, -1, -2, -3, -4, -5]); 106 | deepEqual(Enumerable.rangeTo(1, 5, 3).toArray(), [1, 4]); 107 | deepEqual(Enumerable.rangeTo(1, -5, 3).toArray(), [1, -2, -5]); 108 | deepEqual(Enumerable.rangeTo(1, -6, 3).toArray(), [1, -2, -5]); 109 | 110 | deepEqual(Enumerable.rangeTo(4, 4).toArray(), [4]); 111 | deepEqual(Enumerable.rangeTo(4, 4, 3).toArray(), [4]); 112 | 113 | strictNotEqual(Enumerable.rangeTo(4, 4), 4); 114 | strictNotEqual(Enumerable.rangeTo(4, 4, 3), 4); 115 | }); 116 | 117 | test("repeat", function () { 118 | let actual = Enumerable.repeat("temp").take(3).toArray(); 119 | deepEqual(actual, ["temp", "temp", "temp"]); 120 | actual = Enumerable.repeat("temp", 5).toArray(); 121 | deepEqual(actual, ["temp", "temp", "temp", "temp", "temp"]); 122 | }); 123 | 124 | test("repeatWithFinalize", function () { 125 | var fin; 126 | let actual = Enumerable.repeatWithFinalize( 127 | function () { return "temp"; }, 128 | function () { fin = "final"; }) 129 | .take(3).toArray(); 130 | deepEqual(actual, ["temp", "temp", "temp"]); 131 | equal("final", fin); 132 | }); 133 | 134 | test("generate", function () { 135 | let actual = Enumerable.generate(function () { return "temp" }).take(3).toArray(); 136 | deepEqual(actual, ["temp", "temp", "temp"]); 137 | actual = Enumerable.generate(function () { return "temp" }, 5).toArray(); 138 | deepEqual(actual, ["temp", "temp", "temp", "temp", "temp"]); 139 | }); 140 | 141 | test("toInfinity", function () { 142 | let actual = Enumerable.toInfinity().where("i=>i%2==0").take(10).toArray(); 143 | deepEqual(actual, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]); 144 | actual = Enumerable.toInfinity(101).take(5).toArray(); 145 | deepEqual(actual, [101, 102, 103, 104, 105]); 146 | actual = Enumerable.toInfinity(101, 5).take(5).toArray(); 147 | deepEqual(actual, [101, 106, 111, 116, 121]); 148 | }); 149 | 150 | test("toNegativeInfinity", function () { 151 | let actual = Enumerable.toNegativeInfinity().where("i=>i%2==0").take(10).toArray(); 152 | deepEqual(actual, [0, -2, -4, -6, -8, -10, -12, -14, -16, -18]); 153 | actual = Enumerable.toNegativeInfinity(3).take(10).toArray(); 154 | deepEqual(actual, [3, 2, 1, 0, -1, -2, -3, -4, -5, -6]); 155 | actual = Enumerable.toNegativeInfinity(3, 5).take(4).toArray(); 156 | deepEqual(actual, [3, -2, -7, -12]); 157 | }); 158 | 159 | test("unfold", function () { 160 | let actual = Enumerable.unfold(5, "$+3").take(5).toArray(); 161 | deepEqual(actual, [5, 8, 11, 14, 17]); 162 | }); 163 | 164 | test("defer", function () { 165 | var xs = []; 166 | 167 | var r = Enumerable.range(1, 5) 168 | .doAction(function (x) { xs.push(x); }); 169 | 170 | var de = Enumerable.defer(function () { return r; }); 171 | 172 | equal(xs.length, 0); 173 | 174 | deepEqual(de.toArray(), [1, 2, 3, 4, 5]); 175 | deepEqual(xs, [1, 2, 3, 4, 5]); 176 | }); 177 | -------------------------------------------------------------------------------- /test/errorHandling.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, equal } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("ErrorHandling"); 5 | 6 | test("catchError", function () 7 | { 8 | var msg; 9 | let actual = Enumerable.range(1, 10) 10 | .select(function (i) 11 | { 12 | if (i == 5) throw new Error("aiueo"); 13 | return i; 14 | }) 15 | .catchError(function (e) 16 | { 17 | msg = e.message; 18 | }) 19 | .toArray(); 20 | deepEqual(actual, [1, 2, 3, 4]); 21 | equal(msg,"aiueo"); 22 | }); 23 | 24 | test("finallyAction", function () 25 | { 26 | var msg; 27 | let actual = Enumerable.range(1, 10) 28 | .select(function (i) 29 | { 30 | if (i == 5) throw new Error("aiueo"); 31 | return i; 32 | }) 33 | .catchError(function (e) 34 | { 35 | msg = e.message; 36 | }) 37 | .finallyAction(function (f) 38 | { 39 | msg += "f"; 40 | }) 41 | .toArray(); 42 | deepEqual(actual, [1, 2, 3, 4]); 43 | equal(msg, "aiueof"); 44 | }); 45 | -------------------------------------------------------------------------------- /test/functional.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, equal } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Functional"); 5 | 6 | test("letBind", function () 7 | { 8 | var sum = Enumerable.range(1, 10) 9 | .letBind(function (e) 10 | { 11 | return e.zip(e, function (a, b) { return { a: a, b: b} }); 12 | }) 13 | .select("$.a + $.b") 14 | .sum(); 15 | equal(sum, 110); 16 | 17 | var l1 = Enumerable.from([1, 2, 3, 4, 5]).letBind(function (a) { 18 | return Enumerable.from(a).zip(Enumerable.from(a).skip(1), function (x, y) { 19 | return x + ':' + y; 20 | }); 21 | }).toArray(); 22 | 23 | deepEqual(l1, ['1:2', '2:3', '3:4', '4:5']); 24 | 25 | var l2 = Enumerable.range(1, 5).letBind(function (a) { 26 | return Enumerable.from(a).zip(Enumerable.from(a).skip(1), function (x, y) { 27 | return x + ':' + y; 28 | }); 29 | }).toArray(); 30 | 31 | deepEqual(l2, ['1:2', '2:3', '3:4', '4:5']); 32 | 33 | deepEqual(l1, l2); 34 | }); 35 | 36 | test("share", function () 37 | { 38 | var share = Enumerable.range(1, 10).share(); 39 | var ar1 = share.take(4).toArray(); 40 | var ar2 = share.toArray(); 41 | var ar3 = share.toArray(); 42 | deepEqual(ar1, [1, 2, 3, 4]); 43 | deepEqual(ar2, [5, 6, 7, 8, 9, 10]); 44 | deepEqual(ar3, []); 45 | }); 46 | 47 | test("memoize", function () 48 | { 49 | var count = 0; 50 | var mem = Enumerable.range(1, 5) 51 | .select(function (x) { count++; return x; }) 52 | .memoize(); 53 | var ar1 = mem.toArray(); 54 | var ar2 = mem.toArray(); 55 | deepEqual(ar1, [1, 2, 3, 4, 5]); 56 | deepEqual(ar2, [1, 2, 3, 4, 5]); 57 | equal(5, count); 58 | 59 | mem = Enumerable.from([1, 2, undefined, 3, 4]) 60 | .memoize(); 61 | 62 | ar1 = mem.toArray(); 63 | ar2 = mem.toArray(); 64 | deepEqual(ar1, [1, 2, undefined, 3, 4]); 65 | deepEqual(ar2, [1, 2, undefined, 3, 4]); 66 | }); 67 | -------------------------------------------------------------------------------- /test/grouping.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Grouping"); 5 | 6 | var fileList = ["temp.xls", "temp2.xls", "temp.pdf", "temp.jpg", "temp2.pdf", "temp3.xls"]; 7 | 8 | test("groupBy", function () 9 | { 10 | let actual = Enumerable.from(fileList) 11 | .groupBy("file=>file.match(/\\.(.+$)/)[1]") 12 | .select("{key:$.key(),value:$.toArray()}") 13 | .toArray(); 14 | let expected = [{ key: "xls", value: ["temp.xls", "temp2.xls", "temp3.xls"] }, 15 | { key: "pdf", value: ["temp.pdf", "temp2.pdf"] }, 16 | { key: "jpg", value: ["temp.jpg"]}]; 17 | deepEqual(actual, expected); 18 | 19 | actual = Enumerable.from(fileList) 20 | .groupBy("file=>file.match(/\\.(.+$)/)[1]", "file=>file.match(/(^.+)\\..+$/)[1]") 21 | .select("{key:$.key(),value:$.toArray()}") 22 | .toArray(); 23 | expected = [{ key: "xls", value: ["temp", "temp2", "temp3"] }, 24 | { key: "pdf", value: ["temp", "temp2"] }, 25 | { key: "jpg", value: ["temp"]}]; 26 | deepEqual(actual, expected); 27 | 28 | actual = Enumerable.from(fileList).groupBy("file=>file.match(/\\.(.+$)/)[1]", 29 | "file=>file", 30 | "ext,group => {extension:ext,count:group.count(),files:group.toArray()}") 31 | .toArray(); 32 | expected = [{ extension: "xls", count: 3, files: ["temp.xls", "temp2.xls", "temp3.xls"] }, 33 | { extension: "pdf", count: 2, files: ["temp.pdf", "temp2.pdf"] }, 34 | { extension: "jpg", count: 1, files: ["temp.jpg"]}]; 35 | deepEqual(actual, expected); 36 | 37 | var objects = [ 38 | { Date: new Date(2000, 1, 1), Id: 1 }, 39 | { Date: new Date(2010, 5, 5), Id: 2 }, 40 | { Date: new Date(2000, 1, 1), Id: 3 } 41 | ] 42 | actual = Enumerable.from(objects) 43 | .groupBy("$.Date", "$.Id", 44 | function (key, group) { return key.getFullYear() + "-" + group.toJoinedString(',') }, 45 | function (key) { return key.toString() }) 46 | .toArray(); 47 | expected = ["2000-1,3", "2010-2"] 48 | deepEqual(actual, expected); 49 | }); 50 | 51 | test("partitionBy", function () 52 | { 53 | let actual = Enumerable.from(fileList) 54 | .partitionBy("file=>file.match(/\\.(.+$)/)[1]") 55 | .select("{key:$.key(),value:$.toArray()}") 56 | .toArray(); 57 | let expected = [{ key: "xls", value: ["temp.xls", "temp2.xls"] }, 58 | { key: "pdf", value: ["temp.pdf"] }, 59 | { key: "jpg", value: ["temp.jpg"] }, 60 | { key: "pdf", value: ["temp2.pdf"] }, 61 | { key: "xls", value: ["temp3.xls"] } 62 | ]; 63 | deepEqual(actual, expected); 64 | 65 | actual = Enumerable.from(fileList) 66 | .partitionBy("file=>file.match(/\\.(.+$)/)[1]", "file=>file.match(/(^.+)\\..+$/)[1]") 67 | .select("{key:$.key(),value:$.toArray()}") 68 | .toArray(); 69 | expected = [{ key: "xls", value: ["temp", "temp2"] }, 70 | { key: "pdf", value: ["temp"] }, 71 | { key: "jpg", value: ["temp"] }, 72 | { key: "pdf", value: ["temp2"] }, 73 | { key: "xls", value: ["temp3"] } 74 | ]; 75 | deepEqual(actual, expected); 76 | 77 | actual = Enumerable.from(fileList) 78 | .partitionBy("file=>file.match(/\\.(.+$)/)[1]", 79 | "file=>file", 80 | "ext,group=>{extension:ext,count:group.count(),files:group.toArray()}") 81 | .toArray(); 82 | expected = [{ extension: "xls", count: 2, files: ["temp.xls", "temp2.xls"] }, 83 | { extension: "pdf", count: 1, files: ["temp.pdf"] }, 84 | { extension: "jpg", count: 1, files: ["temp.jpg"] }, 85 | { extension: "pdf", count: 1, files: ["temp2.pdf"] }, 86 | { extension: "xls", count: 1, files: ["temp3.xls"] } 87 | ]; 88 | deepEqual(actual, expected); 89 | 90 | var objects = [ 91 | { Date: new Date(2000, 1, 1), Id: 1 }, 92 | { Date: new Date(2000, 1, 1), Id: 2 }, 93 | { Date: new Date(2010, 5, 5), Id: 3 }, 94 | { Date: new Date(2000, 1, 1), Id: 4 }, 95 | { Date: new Date(2010, 5, 5), Id: 5 }, 96 | { Date: new Date(2010, 5, 5), Id: 6 } 97 | ] 98 | actual = Enumerable.from(objects) 99 | .partitionBy("$.Date", "$.Id", 100 | function (key, group) { return key.getFullYear() + "-" + group.toJoinedString(',') }, 101 | function (key) { return key.toString() }) 102 | .toArray(); 103 | expected = ["2000-1,2", "2010-3", "2000-4", "2010-5,6"] 104 | deepEqual(actual, expected); 105 | }); 106 | 107 | test("buffer", function () 108 | { 109 | let actual = Enumerable.range(1, 10).buffer("3").toArray(); 110 | let expected = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]; 111 | deepEqual(actual, expected); 112 | }); 113 | -------------------------------------------------------------------------------- /test/iterator.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, equal } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Iterator"); 5 | 6 | test("for..of", function () { 7 | let actual = []; 8 | for (var a of Enumerable.from([1, 2, 3])) { 9 | actual.push(a); 10 | } 11 | deepEqual(actual, [1, 2, 3]); 12 | }); 13 | 14 | test("Symbol.iterator", function () 15 | { 16 | let actual = [1,2,3,4]; 17 | let expected = Array.from(Enumerable.from(actual)); 18 | deepEqual(actual, expected); 19 | let actual2 = actual.map(function(x) { return x * 2 }); // [2,4,6,8]; 20 | expected = Enumerable.from(actual).select(function(x) { return x * 2 }); 21 | deepEqual(actual2, Array.from(expected)); 22 | }); 23 | 24 | test("from Generator", function () { 25 | function* words() { 26 | yield "abc"; 27 | yield "def"; 28 | } 29 | 30 | deepEqual(Enumerable.from(words()).toArray(), ["abc", "def"]); 31 | 32 | let actual = []; 33 | for (var a of Enumerable.from(words())) { 34 | actual.push(a); 35 | } 36 | deepEqual(actual, ["abc", "def"]); 37 | }); 38 | 39 | test("from Iterable object", function () { 40 | const map = new Map(); 41 | 42 | map.set(1, 2); 43 | map.set(2, 4); 44 | 45 | deepEqual(Enumerable 46 | .from(map) 47 | .select(item => ({key: item[0], value: item[1]})) 48 | .select(item=>item.key) 49 | .toArray(), 50 | [1, 2]); 51 | 52 | let actual = []; 53 | for (var a of map) { 54 | actual.push(a[0]); 55 | } 56 | deepEqual(actual, [1, 2]); 57 | 58 | const set = new Set([1, 2, 3]) 59 | equal(Enumerable.from(set).first(), 1) 60 | }); 61 | 62 | test("from Iterator object", function () { 63 | const n = { 64 | // This is just a simple replacement for the data structure that needs to be traversed. 65 | // It may actually be a tree or other data structure implemented by a custom traversal. 66 | nums: [1, 2, 3], 67 | 68 | [Symbol.iterator]() { 69 | let idx = 0; 70 | return { 71 | next: () => { 72 | if (idx < this.nums.length) { 73 | return { 74 | value: this.nums[idx++], 75 | done: false, 76 | }; 77 | } else return { 78 | value: undefined, 79 | done: true, 80 | }; 81 | }, 82 | }; 83 | } 84 | } 85 | 86 | deepEqual(Enumerable.from(n[Symbol.iterator]()).toArray(), [1, 2, 3]); 87 | 88 | let actual = []; 89 | for (var a of n) { 90 | actual.push(a); 91 | } 92 | deepEqual(actual, [1, 2, 3]); 93 | }); 94 | 95 | test("reusable iterator", function () { 96 | const set = new Set([1, 2, 3]) 97 | 98 | let a = Enumerable.from(set.entries()); 99 | 100 | deepEqual(a.toArray(), [[1, 1], [2, 2], [3, 3]]); 101 | deepEqual(a.toArray(), []); 102 | 103 | let b = Enumerable.from(() => set.entries()); 104 | 105 | deepEqual(b.toArray(), [[1, 1], [2, 2], [3, 3]]); 106 | deepEqual(b.toArray(), [[1, 1], [2, 2], [3, 3]]); 107 | 108 | let c = Enumerable.from(() => ['x', 'y', 'z']); 109 | 110 | deepEqual(c.toArray(), ['x', 'y', 'z']); 111 | deepEqual(c.toArray(), ['x', 'y', 'z']); 112 | }); 113 | -------------------------------------------------------------------------------- /test/join.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Join"); 5 | 6 | test("join", function () 7 | { 8 | var math = { yamada: 100, tanaka: 80, yoshida: 94 }; 9 | var english = { yamada: 73, yoshida: 26, tanaka: 99 }; 10 | let actual = Enumerable.from(math) 11 | .join(english, "outer=>outer.key", "inner=>inner.key", 12 | "o,i=>{Name:o.key,Math:o.value,English:i.value}") 13 | .toArray(); 14 | let expected = [{ Name: "yamada", Math: 100, English: 73 }, 15 | { Name: "tanaka", Math: 80, English: 99 }, 16 | { Name: "yoshida", Math: 94, English: 26}]; 17 | deepEqual(actual, expected); 18 | 19 | actual = Enumerable.from(math) 20 | .join(english, "outer=>outer", "inner=>inner", 21 | "o,i=>{Name:o.key,Math:o.value,English:i.value}", "$.key") 22 | .toArray(); 23 | expected = [{ Name: "yamada", Math: 100, English: 73 }, 24 | { Name: "tanaka", Math: 80, English: 99 }, 25 | { Name: "yoshida", Math: 94, English: 26}]; 26 | deepEqual(actual, expected); 27 | 28 | actual = Enumerable.from(math) 29 | .join(english, "outer=>outer.key", "inner=>inner.key", 30 | "(o,i)=>{return {Name:o.key, Math:o.value, English:i.value}}") 31 | .toArray(); 32 | 33 | expected = [{ Name: "yamada", Math: 100, English: 73 }, 34 | { Name: "tanaka", Math: 80, English: 99 }, 35 | { Name: "yoshida", Math: 94, English: 26}]; 36 | 37 | deepEqual(actual, expected); 38 | 39 | actual = Enumerable.from(math) 40 | .join(english, "outer=>outer.key", "inner=>inner.key", 41 | "(o,i)=>{returnVal: o.key}") 42 | .toArray(); 43 | 44 | expected = [{ returnVal: "yamada" }, { returnVal: "tanaka" }, { returnVal: "yoshida"}]; 45 | 46 | deepEqual(actual, expected); 47 | }); 48 | 49 | test("groupJoin", function () 50 | { 51 | var array1 = [3, 3, 4, 5, 6]; 52 | var array2 = [2, 4, 5, 6, 6]; 53 | let actual = Enumerable.from(array1) 54 | .groupJoin(array2, " i => i", " i => i", 55 | function (outer, collection) 56 | { 57 | return { 58 | outer: outer, 59 | collection: collection.toArray() 60 | } 61 | }) 62 | .toArray(); 63 | let expected = [{ outer: 3, collection: [] }, 64 | { outer: 3, collection: [] }, 65 | { outer: 4, collection: [4] }, 66 | { outer: 5, collection: [5] }, 67 | { outer: 6, collection: [6, 6]}]; 68 | deepEqual(actual, expected); 69 | 70 | actual = Enumerable.from(array1) 71 | .groupJoin(array2, " i => i", " i => i", 72 | function (outer, collection) 73 | { 74 | return { 75 | outer: outer, 76 | collection: collection.toArray() 77 | } 78 | }, 79 | function (key) { return key % 2 == 0; }) 80 | .toArray(); 81 | expected = [{ outer: 3, collection: [5] }, 82 | { outer: 3, collection: [5] }, 83 | { outer: 4, collection: [2, 4, 6, 6] }, 84 | { outer: 5, collection: [5] }, 85 | { outer: 6, collection: [2, 4, 6, 6]}]; 86 | deepEqual(actual, expected); 87 | }); 88 | 89 | test("leftJoin", function () 90 | { 91 | var math = { yamada: 100, tanaka: 80, yoshida: 94 }; 92 | var english = { yamada: 73, tanaka: 99 }; 93 | let actual = Enumerable.from(math) 94 | .leftJoin(english, "outer=>outer.key", "inner=>inner.key", 95 | (o,i) => ({Name:o.key,Math:o.value,English:i == null ? null : i.value})) 96 | .toArray(); 97 | let expected = [{ Name: "yamada", Math: 100, English: 73 }, 98 | { Name: "tanaka", Math: 80, English: 99 }, 99 | { Name: "yoshida", Math: 94, English: null}]; 100 | deepEqual(actual, expected); 101 | 102 | actual = Enumerable.from(math) 103 | .leftJoin(english, "outer=>outer", "inner=>inner", 104 | (o,i) => ({Name:o.key,Math:o.value,English:i == null ? null : i.value}), 105 | "$.key") 106 | .toArray(); 107 | expected = [{ Name: "yamada", Math: 100, English: 73 }, 108 | { Name: "tanaka", Math: 80, English: 99 }, 109 | { Name: "yoshida", Math: 94, English: null}]; 110 | deepEqual(actual, expected); 111 | }); -------------------------------------------------------------------------------- /test/ordering.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, notDeepEqual, ok, strictEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Ordering"); 5 | 6 | var expected, actual; 7 | 8 | var list = [ 9 | { a: 2, b: 4, c: 1 }, 10 | { a: 2, b: 3, c: 7 }, 11 | { a: 6, b: 6, c: 3 }, 12 | { a: 4, b: 4, c: 5 }, 13 | { a: 7, b: 3, c: 2 }, 14 | { a: 4, b: 4, c: 3 } 15 | ]; 16 | 17 | var strlist = [ 18 | { a: "a", b: "z", c: "b" }, 19 | { a: "z", b: "e", c: "e" }, 20 | { a: "n", b: "d", c: "q" }, 21 | { a: "a", b: "c", c: "k" }, 22 | { a: "n", b: "d", c: "o" } 23 | ]; 24 | 25 | test("orderBy", function () { 26 | actual = Enumerable.from([1, 51, 7, 823, 85, 31, 51, 99]) 27 | .orderBy("i=>i") 28 | .toArray(); 29 | deepEqual(actual, [1, 7, 31, 51, 51, 85, 99, 823]); 30 | 31 | deepEqual(Enumerable.rangeTo(10, 1).orderBy("$%5").toArray(), [10, 5, 6, 1, 7, 2, 8, 3, 9, 4]); 32 | 33 | actual = ['b', 'a', 'd', 'c']; 34 | deepEqual(Enumerable.from(actual).orderBy().toArray(), ['a', 'b', 'c', 'd']); 35 | deepEqual(Enumerable.from(actual).orderBy(x=>x, "(x,y)=>x.localeCompare(y)").toArray(), ['a', 'b', 'c', 'd']); 36 | deepEqual(Enumerable.from(actual).orderBy(x=>x, (x,y)=>(x < y) ? 1 : (x == y) ? 0 : -1).toArray(), ['d', 'c', 'b', 'a']); 37 | deepEqual(Enumerable.from(actual).orderBy(x=>x, (x,y)=>(x < y) ? 1 : -1).toArray(), ['d', 'c', 'b', 'a']); 38 | }); 39 | 40 | test("orderByDescending", function () { 41 | actual = Enumerable.from([1, 51, 7, 823, 85, 31, 51, 99]) 42 | .orderByDescending("i=>i") 43 | .toArray(); 44 | deepEqual(actual, [823, 99, 85, 51, 51, 31, 7, 1]); 45 | 46 | deepEqual(Enumerable.rangeTo(1, 10).orderByDescending("$%5").toArray(), [4, 9, 3, 8, 2, 7, 1, 6, 5, 10]); 47 | 48 | actual = ['b', 'a', 'd', 'c']; 49 | deepEqual(Enumerable.from(actual) 50 | .orderByDescending(x=>x, (x, y)=>x < y ? -1 : +(x > y)).toArray(), ['d', 'c', 'b', 'a']); 51 | }); 52 | 53 | test("thenBy", function () { 54 | actual = Enumerable.from(list) 55 | .orderBy("l=>l.a") 56 | .thenBy("l=>l.b") 57 | .thenBy("l=>l.c") 58 | .toArray(); 59 | expected = [ 60 | { a: 2, b: 3, c: 7 }, 61 | { a: 2, b: 4, c: 1 }, 62 | { a: 4, b: 4, c: 3 }, 63 | { a: 4, b: 4, c: 5 }, 64 | { a: 6, b: 6, c: 3 }, 65 | { a: 7, b: 3, c: 2 } 66 | ]; 67 | deepEqual(actual, expected); 68 | 69 | actual = Enumerable.from(strlist) 70 | .orderBy("l=>l.a") 71 | .thenBy("l=>l.b") 72 | .thenBy("l=>l.c") 73 | .toArray(); 74 | expected = [ 75 | { a: "a", b: "c", c: "k" }, 76 | { a: "a", b: "z", c: "b" }, 77 | { a: "n", b: "d", c: "o" }, 78 | { a: "n", b: "d", c: "q" }, 79 | { a: "z", b: "e", c: "e" } 80 | ]; 81 | deepEqual(actual, expected); 82 | 83 | actual = Enumerable.from(strlist) 84 | .orderBy(l=>l.a) 85 | .thenBy(l=>l, (x,y) => x.b < y.b ? -1 : x.b > y.b ? 1 : x.c < y.c ? -1 : +(x.c > y.c)) 86 | .toArray(); 87 | 88 | deepEqual(actual, expected); 89 | }); 90 | 91 | test("thenByDescending", function () { 92 | actual = Enumerable.from(list) 93 | .orderByDescending("l=>l.a") 94 | .thenByDescending("l=>l.b") 95 | .thenByDescending("l=>l.c") 96 | .toArray(); 97 | expected = [ 98 | { a: 7, b: 3, c: 2 }, 99 | { a: 6, b: 6, c: 3 }, 100 | { a: 4, b: 4, c: 5 }, 101 | { a: 4, b: 4, c: 3 }, 102 | { a: 2, b: 4, c: 1 }, 103 | { a: 2, b: 3, c: 7 } 104 | ]; 105 | deepEqual(actual, expected); 106 | 107 | actual = Enumerable.from(strlist) 108 | .orderByDescending("l=>l.a") 109 | .thenByDescending("l=>l.b") 110 | .thenByDescending("l=>l.c") 111 | .toArray(); 112 | expected = [ 113 | { a: "z", b: "e", c: "e" }, 114 | { a: "n", b: "d", c: "q" }, 115 | { a: "n", b: "d", c: "o" }, 116 | { a: "a", b: "z", c: "b" }, 117 | { a: "a", b: "c", c: "k" } 118 | ]; 119 | deepEqual(actual, expected); 120 | }); 121 | 122 | test("reverse", function () { 123 | actual = Enumerable.from([1, 51, 7, 823, 85, 31, 51, 99]) 124 | .reverse() 125 | .toArray(); 126 | deepEqual(actual, [99, 51, 31, 85, 823, 7, 51, 1]); 127 | }); 128 | 129 | test("shuffle", function () { 130 | var array = [1, 51, 7, 823, 85, 31, 51, 99]; 131 | var shuffled = Enumerable.from(array).shuffle().toArray(); 132 | notDeepEqual(shuffled, array); 133 | }); 134 | 135 | test("weightedSample", function () { 136 | var result = Enumerable.from([1, 25, 35, 39]).weightedSample() 137 | .take(10000) 138 | .groupBy() 139 | .toObject("$.key()", "$.count()"); 140 | 141 | ok((function (x) { return 0 < x && x < 200 })(result[1])); 142 | ok((function (x) { return 2300 < x && x < 2700 })(result[25])); 143 | ok((function (x) { return 3300 < x && x < 3700 })(result[35])); 144 | ok((function (x) { return 3700 < x && x < 4100 })(result[39])); 145 | 146 | strictEqual(Enumerable.from(result).sum(function (x) { return x.value }), 10000); 147 | 148 | result = Enumerable.from([1, 99]).weightedSample().take(10000).groupBy().toObject("$.key()", "$.count()"); 149 | ok((function (x) { return 0 < x && x < 200 })(result[1])); 150 | ok((function (x) { return 9800 < x && x < 10000 })(result[99])); 151 | 152 | result = Enumerable.from([0, 1]).weightedSample().take(10000).groupBy().toObject("$.key()", "$.count()"); 153 | ok(result[0] === undefined); 154 | strictEqual(result[1], 10000); 155 | }); 156 | -------------------------------------------------------------------------------- /test/paging.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, strictEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Paging"); 5 | 6 | test("elementAt", function () { 7 | let actual = Enumerable.range(1, 10).elementAt(5); 8 | strictEqual(actual, 6); 9 | }); 10 | 11 | test("elementAtOrDefault", function () { 12 | let actual = Enumerable.range(1, 10).elementAtOrDefault(3, 0); 13 | strictEqual(actual, 4); 14 | actual = Enumerable.range(1, 10).elementAtOrDefault(31, 0); 15 | strictEqual(actual, 0); 16 | 17 | actual = Enumerable.range(1, 10).elementAtOrDefault(3, "foo"); 18 | strictEqual(actual, 4); 19 | actual = Enumerable.range(1, 10).elementAtOrDefault(31, "foo"); 20 | strictEqual(actual, "foo"); 21 | }); 22 | 23 | test("first", function () { 24 | let actual = Enumerable.range(1, 10).first(); 25 | strictEqual(actual, 1); 26 | actual = Enumerable.range(1, 10).first("i=>i*3==6"); 27 | strictEqual(actual, 2); 28 | }); 29 | 30 | test("firstOrDefault", function () { 31 | var nonEmpty = Enumerable.range(1, 10); 32 | var empty = Enumerable.empty(); 33 | 34 | // No arguments. 35 | strictEqual(nonEmpty.firstOrDefault(), 1); 36 | strictEqual(empty.firstOrDefault(), undefined); 37 | 38 | // No predicate. 39 | strictEqual(nonEmpty.firstOrDefault(0), 1); 40 | strictEqual(empty.firstOrDefault(0), 0); 41 | strictEqual(empty.firstOrDefault(undefined), undefined); 42 | 43 | // "null" predicate. 44 | strictEqual(nonEmpty.firstOrDefault(null, 0), 1); 45 | strictEqual(empty.firstOrDefault(null), undefined); // Because "null" is treated as noop predicate. 46 | strictEqual(empty.firstOrDefault(null, 0), 0); 47 | strictEqual(empty.firstOrDefault(null, null), null); 48 | strictEqual(empty.firstOrDefault(null, undefined), undefined); 49 | 50 | // No default value. 51 | strictEqual(nonEmpty.firstOrDefault("i=>true"), 1); 52 | strictEqual(empty.firstOrDefault("i=>true"), undefined); 53 | 54 | // Both arguments. 55 | strictEqual(nonEmpty.firstOrDefault("i=>true", 0), 1); 56 | strictEqual(empty.firstOrDefault("i=>true", 0), 0); 57 | strictEqual(empty.firstOrDefault("i=>true", null), null); 58 | strictEqual(empty.firstOrDefault("i=>true", undefined), undefined); 59 | }); 60 | 61 | test("last", function () { 62 | let actual = Enumerable.range(1, 10).last(); 63 | strictEqual(actual, 10); 64 | 65 | actual = Enumerable.range(1, 10).last("i=>i<6"); 66 | strictEqual(actual, 5); 67 | }); 68 | 69 | test("lastOrDefault", function () { 70 | var nonEmpty = Enumerable.range(1, 10); 71 | var empty = Enumerable.empty(); 72 | 73 | // No arguments. 74 | strictEqual(nonEmpty.lastOrDefault(), 10); 75 | strictEqual(empty.lastOrDefault(), undefined); 76 | 77 | // No predicate. 78 | strictEqual(nonEmpty.lastOrDefault(0), 10); 79 | strictEqual(empty.lastOrDefault(0), 0); 80 | strictEqual(empty.lastOrDefault(undefined), undefined); 81 | 82 | // "null" predicate. 83 | strictEqual(nonEmpty.lastOrDefault(null, 0), 10); 84 | strictEqual(empty.lastOrDefault(null), undefined); // Because "null" is treated as noop predicate. 85 | strictEqual(empty.lastOrDefault(null, 0), 0); 86 | strictEqual(empty.lastOrDefault(null, null), null); 87 | strictEqual(empty.lastOrDefault(null, undefined), undefined); 88 | 89 | // No default value. 90 | strictEqual(nonEmpty.lastOrDefault("i=>true"), 10); 91 | strictEqual(empty.lastOrDefault("i=>true"), undefined); 92 | 93 | // Both arguments. 94 | strictEqual(nonEmpty.lastOrDefault("i=>true", 0), 10); 95 | strictEqual(empty.lastOrDefault("i=>true", 0), 0); 96 | strictEqual(empty.lastOrDefault("i=>true", null), null); 97 | strictEqual(empty.lastOrDefault("i=>true", undefined), undefined); 98 | }); 99 | 100 | test("single", function () { 101 | let actual = Enumerable.range(1, 1).single(); 102 | strictEqual(actual, 1); 103 | 104 | actual = Enumerable.range(1, 10).single("i=>i==6"); 105 | strictEqual(actual, 6); 106 | }); 107 | 108 | test("singleOrDefault", function () { 109 | let actual = Enumerable.range(1, 1).singleOrDefault(null, 0); 110 | strictEqual(actual, 1); 111 | actual = Enumerable.range(1, 10).skip(11).singleOrDefault(null, 0); 112 | strictEqual(actual, 0); 113 | 114 | actual = Enumerable.range(1, 1).singleOrDefault(null, 34); 115 | strictEqual(actual, 1); 116 | actual = Enumerable.range(1, 10).skip(11).singleOrDefault(null, 34); 117 | strictEqual(actual, 34); 118 | 119 | actual = Enumerable.range(1, 10).singleOrDefault("i=>i*3==6", 4); 120 | strictEqual(actual, 2); 121 | actual = Enumerable.range(1, 10).singleOrDefault("i=>i>13", 40); 122 | strictEqual(actual, 40); 123 | 124 | strictEqual(Enumerable.range(1, 1).singleOrDefault(), 1); 125 | strictEqual(Enumerable.range(1, 10).singleOrDefault("i=>i*3==6"), 2); 126 | strictEqual(Enumerable.range(1, 10).singleOrDefault("i=>i>13"), null); 127 | strictEqual(Enumerable.empty().singleOrDefault(), null); 128 | }); 129 | 130 | test("skip", function () { 131 | let actual = Enumerable.range(1, 10).skip(4).toArray(); 132 | deepEqual(actual, [5, 6, 7, 8, 9, 10]); 133 | }); 134 | 135 | test("skipWhile", function () { 136 | let actual = Enumerable.range(1, 10).skipWhile("i=>i<8").toArray(); 137 | deepEqual(actual, [8, 9, 10]); 138 | 139 | actual = Enumerable.range(1, 10).skipWhile("v,i=>i<8").toArray(); 140 | deepEqual(actual, [9, 10]); 141 | }); 142 | 143 | test("take", function () { 144 | let actual = Enumerable.range(1, 10).take(4).toArray(); 145 | deepEqual(actual, [1, 2, 3, 4]); 146 | }); 147 | 148 | test("takeWhile", function () { 149 | let actual = Enumerable.range(1, 10).takeWhile("i=>i<8").toArray(); 150 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7]); 151 | 152 | actual = Enumerable.range(1, 10).takeWhile("v,i=>i<8").toArray(); 153 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8]); 154 | }); 155 | 156 | test("takeExceptLast", function () { 157 | let actual = Enumerable.range(1, 10).takeExceptLast().toArray(); 158 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 159 | actual = Enumerable.range(1, 10).takeExceptLast(3).toArray(); 160 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7]); 161 | actual = Enumerable.range(1, 10).takeExceptLast(-1).toArray(); 162 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 163 | actual = Enumerable.range(1, 10).takeExceptLast(100).toArray(); 164 | deepEqual(actual, []); 165 | }); 166 | 167 | test("takeFromLast", function () { 168 | let actual = Enumerable.range(1, 10).takeFromLast(3).toArray(); 169 | deepEqual(actual, [8, 9, 10]); 170 | actual = Enumerable.range(1, 10).takeFromLast(100).toArray(); 171 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 172 | actual = Enumerable.range(1, 10).takeFromLast(0).toArray(); 173 | deepEqual(actual, []); 174 | actual = Enumerable.range(1, 10).takeFromLast(-10).toArray(); 175 | deepEqual(actual, []); 176 | }); 177 | 178 | test("indexOf", function () { 179 | let actual = Enumerable.range(1, 10).indexOf(3); 180 | strictEqual(actual, 2); 181 | 182 | Enumerable.Utils.extendTo(Array); 183 | 184 | strictEqual([1, 10, 100, 1000, 100, 100].asEnumerable().indexOf(100), 2); 185 | 186 | strictEqual([1, 2, 3, 3, 3, 4, 5].asEnumerable().indexOf(3), 2); 187 | strictEqual([1, 2, 3, 3, 3, 4, 5].asEnumerable().indexOf(function (x) { return x == 3; }), 2); 188 | 189 | Enumerable.Utils.recallFrom(Array); 190 | }); 191 | 192 | test("lastIndexOf", function () { 193 | let actual = Enumerable.from([1, 2, 3, 2, 5]).lastIndexOf(2) 194 | strictEqual(actual, 3); 195 | 196 | Enumerable.Utils.extendTo(Array); 197 | 198 | strictEqual([1, 2, 3, 3, 3, 4, 5].asEnumerable().lastIndexOf(3), 4); 199 | strictEqual([1, 2, 3, 3, 3, 4, 5].asEnumerable().lastIndexOf(function (x) { return x == 3; }), 4); 200 | 201 | Enumerable.Utils.recallFrom(Array); 202 | }); 203 | -------------------------------------------------------------------------------- /test/projection.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Projection"); 5 | 6 | test("traverseDepthFirst", function () { 7 | let actual = Enumerable.make(1).traverseDepthFirst("$+$").take(7).toArray(); 8 | deepEqual(actual, [1, 2, 4, 8, 16, 32, 64]); 9 | actual = Enumerable.make(1).traverseDepthFirst("$+$", "v,nl=>{v:v,nl:nl}").take(3).toArray(); 10 | deepEqual(actual, [{ v: 1, nl: 0 }, { v: 2, nl: 1 }, { v: 4, nl: 2 }]); 11 | }); 12 | 13 | test("traverseBreadthFirst", function () { 14 | let actual = Enumerable.make(1).traverseBreadthFirst("$+$").take(7).toArray(); 15 | deepEqual(actual, [1, 2, 4, 8, 16, 32, 64]); 16 | actual = Enumerable.make(1).traverseBreadthFirst("$+$", "v,nl=>{v:v,nl:nl}").take(3).toArray(); 17 | deepEqual(actual, [{ v: 1, nl: 0 }, { v: 2, nl: 1 }, { v: 4, nl: 2 }]); 18 | }); 19 | 20 | test("flatten", function () { 21 | var array = [1, 31, [431, 41, 5], [1431, 43, [344, 3], 43], 43]; 22 | let actual = Enumerable.from(array).flatten().toArray(); 23 | deepEqual(actual, [1, 31, 431, 41, 5, 1431, 43, 344, 3, 43, 43]); 24 | }); 25 | 26 | test("pairwise", function () { 27 | let actual = Enumerable.range(1, 4).pairwise("prev,next=>{p:prev,n:next}").toArray(); 28 | deepEqual(actual, [{ p: 1, n: 2 }, { p: 2, n: 3 }, { p: 3, n: 4 }]); 29 | }); 30 | 31 | test("scan", function () { 32 | let actual = Enumerable.range(1, 10).scan("a,b=>a+b").toArray(); 33 | deepEqual(actual, [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]); 34 | var seed = 100; 35 | actual = Enumerable.range(1, 10).scan(seed, "a,b=>a+b").toArray(); 36 | deepEqual(actual, [100, 101, 103, 106, 110, 115, 121, 128, 136, 145, 155]); 37 | }); 38 | 39 | test("select", function () { 40 | let actual = Enumerable.range(1, 10).select("i=>i*10").toArray(); 41 | deepEqual(actual, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]); 42 | actual = Enumerable.range(1, 10).select("i,index=>i*10+index").toArray(); 43 | deepEqual(actual, [10, 21, 32, 43, 54, 65, 76, 87, 98, 109]); 44 | }); 45 | 46 | test("selectMany", function () { 47 | // let actual = Enumerable.range(1, 5).selectMany("i=>this.repeat(i,2)").toArray(); 48 | // deepEqual(actual, [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]); 49 | // actual = Enumerable.range(1, 5).selectMany("i,index=>this.repeat(i,index+1)").toArray(); 50 | // deepEqual(actual, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]); 51 | // actual = Enumerable.range(1, 5).selectMany("i=>this.repeat(i,2)", "i=>i*10").toArray(); 52 | // deepEqual(actual, [10, 10, 20, 20, 30, 30, 40, 40, 50, 50]); 53 | // actual = Enumerable.range(1, 5).selectMany("i,index=>this.repeat(i,index+1)", "i=>i*10").toArray(); 54 | // deepEqual(actual, [10, 20, 20, 30, 30, 30, 40, 40, 40, 40, 50, 50, 50, 50, 50]); 55 | 56 | let actual = Enumerable.range(1, 5) 57 | .selectMany(function(i) { return Enumerable.repeat(i, 2); }) 58 | .toArray(); 59 | deepEqual(actual, [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]); 60 | actual = Enumerable.range(1, 5) 61 | .selectMany(function(i, index) { return Enumerable.repeat(i, index + 1); }) 62 | .toArray(); 63 | deepEqual(actual, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]); 64 | actual = Enumerable.range(1, 5) 65 | .selectMany(function(i) { return Enumerable.repeat(i, 2); }, "i=>i*10") 66 | .toArray(); 67 | deepEqual(actual, [10, 10, 20, 20, 30, 30, 40, 40, 50, 50]); 68 | actual = Enumerable.range(1, 5) 69 | .selectMany(function(i, index) { return Enumerable.repeat(i, index + 1); }, "i=>i*10") 70 | .toArray(); 71 | deepEqual(actual, [10, 20, 20, 30, 30, 30, 40, 40, 40, 40, 50, 50, 50, 50, 50]); 72 | }); 73 | 74 | test("where", function () { 75 | let actual = Enumerable.range(1, 10).where("i=>i%2==0").toArray(); 76 | deepEqual(actual, [2, 4, 6, 8, 10]); 77 | actual = Enumerable.range(1, 10).where("i,index=>(i+index)%3==0").toArray(); 78 | deepEqual(actual, [2, 5, 8]); 79 | }); 80 | 81 | test("choose", function () { 82 | deepEqual(Enumerable.range(1, 10).choose(function (x) { 83 | return x % 2 == 0 ? null : x; 84 | }).toArray(), [1, 3, 5, 7, 9]); 85 | 86 | deepEqual(Enumerable.range(1, 10).choose().toArray(), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 87 | }); 88 | 89 | test("ofType", function () { 90 | var seq = Enumerable.from([1, 2, "hoge", "3", 4, true]); 91 | deepEqual(seq.ofType(Number).toArray(), [1, 2, 4]); 92 | deepEqual(seq.ofType(String).toArray(), ["hoge", "3"]); 93 | deepEqual(seq.ofType(Boolean).toArray(), [true]); 94 | 95 | var Cls = function (val) { this.val = val; } 96 | seq = Enumerable.from([new Cls("a"), new Cls("b"), 1, 2, new Cls("c"), 3]); 97 | deepEqual(seq.ofType(Cls).select("$.val").toArray(), ["a", "b", "c"]); 98 | }); 99 | 100 | test("zip", function () { 101 | let actual = Enumerable.range(1, 10).zip(Enumerable.range(20, 5), "outer,inner=>outer+inner").toArray(); 102 | deepEqual(actual, [21, 23, 25, 27, 29]); 103 | actual = Enumerable.range(1, 10).zip(Enumerable.range(20, 5), "outer,inner,index=>outer+inner+index").toArray(); 104 | deepEqual(actual, [21, 24, 27, 30, 33]); 105 | }); 106 | 107 | test("zip2", function () { 108 | Enumerable.Utils.extendTo(Array); 109 | 110 | deepEqual([1, 2, 3] 111 | .zip( 112 | [-3, 4, 10], 113 | [5, 6, 7], 114 | function (x, y, z) { return x * y * z; }).toArray(), 115 | [-15, 48, 210]); 116 | 117 | deepEqual([1, 2, 3] 118 | .zip( 119 | [-3, 4, 10], 120 | [-3, 4, 10], 121 | function (x, y, z, i) { return i; }).toArray(), 122 | [0, 1, 2]); 123 | 124 | deepEqual([1, 2, 3] 125 | .zip( 126 | [-3, 4, 10], 127 | [-7, 20, 30, 100], 128 | function (x, y, z) { return x * y + z; }).toArray(), 129 | [-10, 28, 60]); 130 | 131 | deepEqual([1, 2, 3] 132 | .zip( 133 | [-3, 4, 10], 134 | [-7, 20, 30, 100], 135 | function (x, y, z, i) { return z + i; }).toArray(), 136 | [-7, 21, 32]); 137 | 138 | Enumerable.Utils.recallFrom(Array); 139 | 140 | deepEqual(Enumerable.from("abc").zip("fghk", "lmnopq", "stuv", "yz0124", "56780", 141 | function (a, b, c, d, e, f) { return a + b + c + d + e + f }).toArray(), 142 | ["aflsy5", "bgmtz6", "chnu07"]); 143 | }); 144 | 145 | test("merge", function () { 146 | Enumerable.Utils.extendTo(Array); 147 | 148 | deepEqual([1, 2, 3].merge([-3, 4, 10]).toArray(), [1, -3, 2, 4, 3, 10]); 149 | 150 | deepEqual([1, 2, 3].merge([-3, 4], [-7, 20, 30, 100]).toArray(), 151 | [1, -3, -7, 2, 4, 20, 3, 30, 100]); 152 | 153 | Enumerable.Utils.recallFrom(Array); 154 | }); 155 | -------------------------------------------------------------------------------- /test/set.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual, ok, strictEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("Set"); 5 | 6 | test("all", function () { 7 | var seq = Enumerable.range(1, 10); 8 | ok(!seq.all("i=>i%2==0")); 9 | ok(seq.all("i=>i<=10")); 10 | }); 11 | 12 | test("any", function () { 13 | var seq = Enumerable.range(1, 10); 14 | var empty = Enumerable.empty(); 15 | ok(seq.any()); 16 | ok(!empty.any()); 17 | ok(seq.any("$==5")); 18 | ok(!seq.any("$==100")); 19 | }); 20 | 21 | test("isEmpty", function () { 22 | var _ = Enumerable.range(1, 10).isEmpty(); 23 | 24 | strictEqual(_, false); 25 | strictEqual(Enumerable.empty().isEmpty(), true); 26 | 27 | Enumerable.Utils.extendTo(Array); 28 | strictEqual([].isEmpty(), true); 29 | Enumerable.Utils.recallFrom(Array); 30 | }); 31 | 32 | test("concat", function () { 33 | let actual = Enumerable.range(1, 3).concat([20, 21, 22]).toArray(); 34 | deepEqual(actual, [1, 2, 3, 20, 21, 22]); 35 | 36 | 37 | deepEqual(Enumerable.range(1, 3).concat([]).toArray(), [1, 2, 3]); 38 | deepEqual(Enumerable.range(1, 3).concat([2, 3], [4, 5]).toArray(), [1, 2, 3, 2, 3, 4, 5]); 39 | var range = Enumerable.rangeTo(3, 5); 40 | deepEqual(range.concat(range, range, range, range).toArray(), Enumerable.repeat(range, 5).selectMany().toArray()); 41 | }); 42 | 43 | test("insert", function () { 44 | let actual = Enumerable.range(1, 5).insert(3, [20, 21, 22]).toArray(); 45 | deepEqual(actual, [1, 2, 3, 20, 21, 22, 4, 5]); 46 | }); 47 | 48 | test("alternate", function () { 49 | Enumerable.Utils.extendTo(Array); 50 | 51 | // single value 52 | deepEqual(Enumerable.empty().alternate(-1).toArray(), []); 53 | 54 | deepEqual([1].alternate(-1).toArray(), [1]); 55 | deepEqual([1, 2].alternate(-1).toArray(), [1, -1, 2]); 56 | deepEqual(Enumerable.range(1, 5).alternate(-1).toArray(), [1, -1, 2, -1, 3, -1, 4, -1, 5]); 57 | deepEqual(Enumerable.range(1, 6).alternate(-1).toArray(), [1, -1, 2, -1, 3, -1, 4, -1, 5, -1, 6]); 58 | 59 | // multiple, array 60 | deepEqual(Enumerable.empty().alternate([-1, -2]).toArray(), []); 61 | deepEqual([1].alternate([-1, -2]).toArray(), [1]); 62 | deepEqual([1, 2].alternate([-1, -2]).toArray(), [1, -1, -2, 2]); 63 | deepEqual(Enumerable.range(1, 5).alternate([-1, -2]).toArray(), [1, -1, -2, 2, -1, -2, 3, -1, -2, 4, -1, -2, 5]); 64 | deepEqual(Enumerable.range(1, 6).alternate([-1, -2]).toArray(), [1, -1, -2, 2, -1, -2, 3, -1, -2, 4, -1, -2, 5, -1, -2, 6]); 65 | 66 | // multiple, enumerable 67 | var seq = Enumerable.rangeTo(-1, -2); 68 | deepEqual(Enumerable.empty().alternate(seq).toArray(), []); 69 | deepEqual([1].alternate(seq).toArray(), [1]); 70 | deepEqual([1, 2].alternate(seq).toArray(), [1, -1, -2, 2]); 71 | deepEqual(Enumerable.range(1, 5).alternate(seq).toArray(), [1, -1, -2, 2, -1, -2, 3, -1, -2, 4, -1, -2, 5]); 72 | deepEqual(Enumerable.range(1, 6).alternate(seq).toArray(), [1, -1, -2, 2, -1, -2, 3, -1, -2, 4, -1, -2, 5, -1, -2, 6]); 73 | }); 74 | 75 | test("contains", function () { 76 | var seq = Enumerable.range(1, 10); 77 | ok(seq.contains(5)); 78 | ok(!seq.contains(13)); 79 | 80 | seq = Enumerable.range(1, 10).select("{test:$%2}"); 81 | ok(seq.contains(1, "$.test")); 82 | ok(!seq.contains(3, "$.test")); 83 | }); 84 | 85 | test("defaultIfEmpty", function () { 86 | let actual = Enumerable.range(1, 10).defaultIfEmpty(199).toArray(); 87 | deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 88 | actual = Enumerable.empty().defaultIfEmpty(199).toArray(); 89 | deepEqual(actual, [199]); 90 | }); 91 | 92 | test("distinct", function () { 93 | let actual = Enumerable.from([1, 3, 5, 6, 6, 3, 4, 3, 2, 9]).distinct().toArray(); 94 | deepEqual(actual, [1, 3, 5, 6, 4, 2, 9]); 95 | actual = Enumerable.range(1, 10).select("{test:$%2}").distinct("$.test").toArray(); 96 | deepEqual(actual, [{ test: 1 }, { test: 0 }]); 97 | }); 98 | 99 | test("distinctUntilChanged", function () { 100 | Enumerable.Utils.extendTo(Array); 101 | 102 | deepEqual([9, 1, 3, 5, 7, 7, 7, 3, 4, 2, 2, 9].distinctUntilChanged().toArray(), [9, 1, 3, 5, 7, 3, 4, 2, 9]); 103 | deepEqual([1, 3, 3, 3, 1, 2, 6, 3, 5, 1] 104 | .select("{test:$}") 105 | .distinctUntilChanged("$.test").toArray(), 106 | [{ test: 1 }, { test: 3 }, { test: 1 }, { test: 2 }, { test: 6 }, { test: 3 }, { test: 5 }, { test: 1 }]); 107 | 108 | deepEqual([1].distinctUntilChanged().toArray(), [1]); 109 | deepEqual([1, 1].distinctUntilChanged().toArray(), [1]); 110 | deepEqual([1, 2].distinctUntilChanged().toArray(), [1, 2]); 111 | 112 | Enumerable.Utils.recallFrom(Array); 113 | }); 114 | 115 | test("except", function () { 116 | let actual = Enumerable.from([1, 3, 5, 6, 6, 3, 4, 3, 2, 9]) 117 | .except([4, 6, 2, 7, 8, 10, 11]) 118 | .toArray(); 119 | deepEqual(actual, [1, 3, 5, 9]); 120 | actual = Enumerable.range(1, 10).select("{test:$%3}") 121 | .except(Enumerable.range(1, 10).select("{test:$%2}"), "$.test") 122 | .toArray(); 123 | deepEqual(actual, [{ test: 2 }]); 124 | }); 125 | 126 | test("intersect", function () { 127 | let actual = Enumerable.from([1, 3, 5, 6, 6, 3, 4, 3, 2, 9]) 128 | .intersect([4, 6, 2, 7, 8, 10, 11]) 129 | .toArray(); 130 | deepEqual(actual, [6, 4, 2]); 131 | actual = Enumerable.range(1, 10).select("{test:$%3}") 132 | .intersect(Enumerable.range(1, 10).select("{test:$%2}"), "$.test") 133 | .toArray(); 134 | deepEqual(actual, [{ test: 1 }, { test: 0 }]); 135 | }); 136 | 137 | test("sequenceEqual", function () { 138 | ok(!Enumerable.from([1, 3, 5, 6, 6, 3, 4, 3, 2, 9]).sequenceEqual([1, 3, 5])); 139 | ok(Enumerable.range(1, 10).sequenceEqual(Enumerable.range(1, 10))); 140 | 141 | ok(!Enumerable.range(1, 10).select("{test:$%3}") 142 | .sequenceEqual(Enumerable.range(1, 10).select("{test:$%2}"), "$.test")); 143 | 144 | ok(Enumerable.range(1, 10) 145 | .select("{test:$%3}") 146 | .distinct("$.test") 147 | .sequenceEqual([{ test: 1 }, { test: 2 }, { test: 0 }], "$.test")); 148 | }); 149 | 150 | test("union", function () { 151 | let actual = Enumerable.from([1, 3, 5, 6, 6, 3, 4, 3, 2, 9]) 152 | .union([4, 6, 2, 7, 8, 10, 11]) 153 | .toArray(); 154 | deepEqual(actual, [1, 3, 5, 6, 4, 2, 9, 7, 8, 10, 11]); 155 | actual = Enumerable.range(1, 3).select("{test:$}") 156 | .union(Enumerable.range(2, 3).select("{test:$}"), "$.test") 157 | .toArray(); 158 | deepEqual(actual, [{ test: 1 }, { test: 2 }, { test: 3 }, { test: 4 }]); 159 | }); 160 | -------------------------------------------------------------------------------- /test/testrunner.js: -------------------------------------------------------------------------------- 1 | import * as testutils from './testutils.js' 2 | 3 | import './action.js' 4 | import './aggregate.js' 5 | import './arrayEnumerable.js' 6 | import './convert.js' 7 | import './dictionary.js' 8 | import './enumerable.js' 9 | import './errorHandling.js' 10 | import './functional.js' 11 | import './grouping.js' 12 | import './iterator.js' 13 | import './join.js' 14 | import './ordering.js' 15 | import './paging.js' 16 | import './projection.js' 17 | import './set.js' 18 | import './whereSelectEnumerable.js' 19 | 20 | testutils.runAll(false); 21 | 22 | if (testutils.getFailedTests().length > 0) 23 | console.log(testutils.getFailedTests()); 24 | -------------------------------------------------------------------------------- /test/testutils.js: -------------------------------------------------------------------------------- 1 | var tests = [] 2 | var failedTests = [] 3 | var currentModule = ""; 4 | var numAssertions = 0; 5 | var assertionIndex = 0; 6 | 7 | function init() { 8 | tests = [] 9 | numAssertions = 0 10 | } 11 | 12 | function test(name, run) { 13 | tests.push({name, run, module: currentModule}) 14 | } 15 | 16 | function testModule(name) { 17 | currentModule = name; 18 | } 19 | 20 | function deepCmp(x, y) { 21 | const kx = Object.keys(x); 22 | const ky = Object.keys(y); 23 | 24 | if (kx.length !== ky.length) 25 | return false; 26 | 27 | for (const key of kx) { 28 | const vx = x[key]; 29 | const vy = y[key]; 30 | const areObjects = vx != null && typeof vx === 'object' && vy != null && typeof vy === 'object'; 31 | 32 | if (areObjects && !deepCmp(vx, vy)) 33 | return false; 34 | 35 | if (!areObjects && vx !== vy) 36 | return false; 37 | } 38 | 39 | return true; 40 | } 41 | 42 | function deepEqual(x, y) { 43 | numAssertions++ 44 | assertionIndex++ 45 | 46 | if (deepCmp(x, y)) 47 | return true; 48 | 49 | throw `[deepEqual] Assertion ${assertionIndex} failed`; 50 | } 51 | 52 | function notDeepEqual(x, y) { 53 | numAssertions++ 54 | assertionIndex++ 55 | 56 | if (deepCmp(x, y)) 57 | throw `[deepEqual] Assertion ${assertionIndex} failed`; 58 | 59 | return true; 60 | } 61 | 62 | function strictEqual(x, y) { 63 | numAssertions++ 64 | assertionIndex++ 65 | 66 | if (x === y) 67 | return true; 68 | 69 | throw `[deepEqual] Assertion ${assertionIndex} failed`; 70 | } 71 | 72 | function strictNotEqual(x, y) { 73 | numAssertions++ 74 | assertionIndex++ 75 | 76 | if (x !== y) 77 | return true; 78 | 79 | throw `[strictNotEqual] Assertion ${assertionIndex} failed`; 80 | } 81 | 82 | function equal(x, y) { 83 | numAssertions++ 84 | assertionIndex++ 85 | 86 | if (x == y) 87 | return true; 88 | 89 | throw `[equal] Assertion ${assertionIndex} failed`; 90 | } 91 | 92 | function notEqual(x, y) { 93 | numAssertions++ 94 | assertionIndex++ 95 | 96 | if (x != y) 97 | return true; 98 | 99 | throw `[notEqual] Assertion ${assertionIndex} failed`; 100 | } 101 | 102 | function ok(condition) { 103 | numAssertions++ 104 | assertionIndex++ 105 | 106 | if (condition == true) 107 | return true; 108 | 109 | throw `[ok] Assertion ${assertionIndex} failed` 110 | } 111 | 112 | function runAll(showExceptions) { 113 | tests.forEach(test => { 114 | try { 115 | assertionIndex = 0; 116 | test.run(); 117 | } 118 | catch (e) { 119 | let info = { module: test.module, test: test.name, assertion: assertionIndex } 120 | failedTests.push(info); 121 | if (showExceptions) { 122 | console.log("Failed: ", info) 123 | throw e; 124 | } 125 | } 126 | }) 127 | 128 | console.log(`Tests: ${tests.length}`) 129 | console.log(`Assertions: ${numAssertions}`) 130 | console.log(`Failed tests: ${failedTests.length}`) 131 | } 132 | 133 | function getFailedTests() { 134 | return failedTests; 135 | } 136 | 137 | export { init, test, testModule, getFailedTests, runAll, deepEqual, notDeepEqual, strictEqual, strictNotEqual, equal, notEqual, ok } 138 | -------------------------------------------------------------------------------- /test/whereSelectEnumerable.js: -------------------------------------------------------------------------------- 1 | import { test, testModule, deepEqual } from './testutils.js' 2 | import Enumerable from '../linq.js' 3 | 4 | testModule("WhereSelectEnumerable"); 5 | 6 | var seq = Enumerable.range(5, 10); // 5-14 7 | var seq2 = Enumerable.range(5, 5); // 5-9 8 | 9 | test("where", function () { 10 | deepEqual(seq.where("$%2==0").toArray(), [6, 8, 10, 12, 14]); 11 | deepEqual(seq.where("$$%2==0").toArray(), [5, 7, 9, 11, 13]); 12 | }); 13 | 14 | test("select", function () { 15 | deepEqual(seq2.select("$*10").toArray(), [50, 60, 70, 80, 90]); 16 | deepEqual(seq2.select("$$*2").toArray(), [0, 2, 4, 6, 8]); 17 | }); 18 | 19 | test("wherewhere", function () { 20 | deepEqual(seq.where("$%2==0").where("$%3==0").toArray(), [6, 12]); 21 | deepEqual(seq.where("$$%2==0").where("$$%2==0").toArray(), [5, 9, 13]); 22 | deepEqual(seq.where("$%2==0").where("$$%2==0").toArray(), [6, 10, 14]); 23 | deepEqual(seq.where("$$%2==0").where("$%3==0").toArray(), [9]); 24 | }); 25 | 26 | test("selectselect", function () { 27 | deepEqual(seq2.select("$*10").select("$*2").toArray(), [100, 120, 140, 160, 180]); 28 | deepEqual(seq2.select("$$*2").select("$+$$*20").toArray(), [0, 22, 44, 66, 88]); 29 | deepEqual(seq2.select("$*10").select("$+$$*2").toArray(), [50, 62, 74, 86, 98]); 30 | deepEqual(seq2.select("$$*2").select("$*10").toArray(), [0, 20, 40, 60, 80]); 31 | }); 32 | 33 | test("whereselect", function () { 34 | deepEqual(seq.where("$%2==0").select("$*2").toArray(), [12, 16, 20, 24, 28]); 35 | deepEqual(seq.where("$%2==0").select("$+$$*2").toArray(), [6, 10, 14, 18, 22]); 36 | deepEqual(seq.where("$$%2==0").select("$*2").toArray(), [10, 14, 18, 22, 26]); 37 | deepEqual(seq.where("$$%2==0").select("$+$$*2").toArray(), [5, 9, 13, 17, 21]); 38 | }); 39 | 40 | test("selectwhere", function () { 41 | deepEqual(seq.select("$*2").where("$%2==0").toArray(), [10, 12, 14, 16, 18, 20, 22, 24, 26, 28]); 42 | deepEqual(seq.select("$+$$*2").where("$%2==0").toArray(), [8, 14, 20, 26, 32]); 43 | deepEqual(seq.select("$*2").where("$$%2==0").toArray(), [10, 14, 18, 22, 26]); 44 | deepEqual(seq.select("$+$$*2").where("$$%2==0").toArray(), [5, 11, 17, 23, 29]); 45 | }); 46 | 47 | test("whereselectwhere", function () { 48 | deepEqual(seq.where("$%2==0").select("$*2").where("$%3==0").toArray(), [12, 24]); 49 | deepEqual(seq.where("$%2==0").select("$+$$*2").where("$$%2==0").toArray(), [6, 14, 22]); 50 | deepEqual(seq.where("$$%2==0").select("$*2").where("$$%2==0").toArray(), [10, 18, 26]); 51 | deepEqual(seq.where("$$%2==0").select("$+$$*2").where("$%3==0").toArray(), [9, 21]); 52 | }); 53 | 54 | test("selectwhereselect", function () { 55 | deepEqual(seq.select("$*2").where("$%2==0").select("$*2").toArray(), [20, 24, 28, 32, 36, 40, 44, 48, 52, 56]); 56 | deepEqual(seq.select("$+$$*2").where("$%2==0").select("$$*2").toArray(), [0, 2, 4, 6, 8]); 57 | deepEqual(seq.select("$*2").where("$$%2==0").select("$*2+$$").toArray(), [20, 29, 38, 47, 56]); 58 | deepEqual(seq.select("$+$$*2").where("$$%2==0").select("$*2").toArray(), [10, 22, 34, 46, 58]); 59 | }); 60 | --------------------------------------------------------------------------------