├── .gitattributes
├── .gitignore
├── .travis.yml
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── linq-tests.web.js
├── mocha.html
├── package.json
├── src
├── Collections.ts
├── Comparers.ts
├── Enumerables.ts
├── Iterators.ts
├── Linq.ts
├── Types.ts
└── Utils.ts
├── test
├── Test.ts
├── TestSuite.ts
├── integration
│ └── IEnumerable.test.ts
└── unitary
│ ├── Dictionary.test.ts
│ ├── Enumerable.test.ts
│ ├── IQueryable.test.ts
│ ├── Iterator.test.ts
│ ├── List.test.ts
│ ├── Stack.test.ts
│ └── Utils.test.ts
├── tsconfig.json
└── tslint.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=auto
3 |
4 | # Declare files that will always have CRLF line endings on checkout.
5 | * text eol=crlf
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | build/
7 | .vscode/
8 | assets/
9 |
10 | *.csproj
11 | *.js
12 | *.map
13 | *.config
14 | *.html
15 |
16 | # User-specific files
17 | *.suo
18 | *.user
19 | *.userosscache
20 | *.sln.docstates
21 |
22 | # User-specific files (MonoDevelop/Xamarin Studio)
23 | *.userprefs
24 |
25 | # Build results
26 | [Dd]ebug/
27 | [Dd]ebugPublic/
28 | [Rr]elease/
29 | [Rr]eleases/
30 | x64/
31 | x86/
32 | bld/
33 | [Bb]in/
34 | [Oo]bj/
35 | [Ll]og/
36 |
37 | # Visual Studio 2015 cache/options directory
38 | .vs/
39 | # Uncomment if you have tasks that create the project's static files in wwwroot
40 | #wwwroot/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUNIT
47 | *.VisualState.xml
48 | TestResult.xml
49 |
50 | # Build Results of an ATL Project
51 | [Dd]ebugPS/
52 | [Rr]eleasePS/
53 | dlldata.c
54 |
55 | # .NET Core
56 | project.lock.json
57 | project.fragment.lock.json
58 | artifacts/
59 | **/Properties/launchSettings.json
60 |
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.pch
68 | *.pdb
69 | *.pgc
70 | *.pgd
71 | *.rsp
72 | *.sbr
73 | *.tlb
74 | *.tli
75 | *.tlh
76 | *.tmp
77 | *.tmp_proj
78 | *.log
79 | *.vspscc
80 | *.vssscc
81 | .builds
82 | *.pidb
83 | *.svclog
84 | *.scc
85 |
86 | # Chutzpah Test files
87 | _Chutzpah*
88 |
89 | # Visual C++ cache files
90 | ipch/
91 | *.aps
92 | *.ncb
93 | *.opendb
94 | *.opensdf
95 | *.sdf
96 | *.cachefile
97 | *.VC.db
98 | *.VC.VC.opendb
99 |
100 | # Visual Studio profiler
101 | *.psess
102 | *.vsp
103 | *.vspx
104 | *.sap
105 |
106 | # TFS 2012 Local Workspace
107 | $tf/
108 |
109 | # Guidance Automation Toolkit
110 | *.gpState
111 |
112 | # ReSharper is a .NET coding add-in
113 | _ReSharper*/
114 | *.[Rr]e[Ss]harper
115 | *.DotSettings.user
116 |
117 | # JustCode is a .NET coding add-in
118 | .JustCode
119 |
120 | # TeamCity is a build add-in
121 | _TeamCity*
122 |
123 | # DotCover is a Code Coverage Tool
124 | *.dotCover
125 |
126 | # Visual Studio code coverage results
127 | *.coverage
128 | *.coveragexml
129 |
130 | # NCrunch
131 | _NCrunch_*
132 | .*crunch*.local.xml
133 | nCrunchTemp_*
134 |
135 | # MightyMoose
136 | *.mm.*
137 | AutoTest.Net/
138 |
139 | # Web workbench (sass)
140 | .sass-cache/
141 |
142 | # Installshield output folder
143 | [Ee]xpress/
144 |
145 | # DocProject is a documentation generator add-in
146 | DocProject/buildhelp/
147 | DocProject/Help/*.HxT
148 | DocProject/Help/*.HxC
149 | DocProject/Help/*.hhc
150 | DocProject/Help/*.hhk
151 | DocProject/Help/*.hhp
152 | DocProject/Help/Html2
153 | DocProject/Help/html
154 |
155 | # Click-Once directory
156 | publish/
157 |
158 | # Publish Web Output
159 | *.[Pp]ublish.xml
160 | *.azurePubxml
161 | # TODO: Comment the next line if you want to checkin your web deploy settings
162 | # but database connection strings (with potential passwords) will be unencrypted
163 | *.pubxml
164 | *.publishproj
165 |
166 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
167 | # checkin your Azure Web App publish settings, but sensitive information contained
168 | # in these scripts will be unencrypted
169 | PublishScripts/
170 |
171 | # NuGet Packages
172 | *.nupkg
173 | # The packages folder can be ignored because of Package Restore
174 | **/packages/*
175 | # except build/, which is used as an MSBuild target.
176 | !**/packages/build/
177 | # Uncomment if necessary however generally it will be regenerated when needed
178 | #!**/packages/repositories.config
179 | # NuGet v3's project.json files produces more ignorable files
180 | *.nuget.props
181 | *.nuget.targets
182 |
183 | # Microsoft Azure Build Output
184 | csx/
185 | *.build.csdef
186 |
187 | # Microsoft Azure Emulator
188 | ecf/
189 | rcf/
190 |
191 | # Windows Store app package directories and files
192 | AppPackages/
193 | BundleArtifacts/
194 | Package.StoreAssociation.xml
195 | _pkginfo.txt
196 |
197 | # Visual Studio cache files
198 | # files ending in .cache can be ignored
199 | *.[Cc]ache
200 | # but keep track of directories ending in .cache
201 | !*.[Cc]ache/
202 |
203 | # Others
204 | ClientBin/
205 | ~$*
206 | *~
207 | *.dbmdl
208 | *.dbproj.schemaview
209 | *.jfm
210 | *.pfx
211 | *.publishsettings
212 | orleans.codegen.cs
213 |
214 | # Since there are multiple workflows, uncomment next line to ignore bower_components
215 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
216 | #bower_components/
217 |
218 | # RIA/Silverlight projects
219 | Generated_Code/
220 |
221 | # Backup & report files from converting an old project file
222 | # to a newer Visual Studio version. Backup files are not needed,
223 | # because we have git ;-)
224 | _UpgradeReport_Files/
225 | Backup*/
226 | UpgradeLog*.XML
227 | UpgradeLog*.htm
228 |
229 | # SQL Server files
230 | *.mdf
231 | *.ldf
232 | *.ndf
233 |
234 | # Business Intelligence projects
235 | *.rdl.data
236 | *.bim.layout
237 | *.bim_*.settings
238 |
239 | # Microsoft Fakes
240 | FakesAssemblies/
241 |
242 | # GhostDoc plugin setting file
243 | *.GhostDoc.xml
244 |
245 | # Node.js Tools for Visual Studio
246 | .ntvs_analysis.dat
247 | node_modules/
248 |
249 | # Typescript v1 declaration files
250 | typings/
251 |
252 | # Visual Studio 6 build log
253 | *.plg
254 |
255 | # Visual Studio 6 workspace options file
256 | *.opt
257 |
258 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
259 | *.vbw
260 |
261 | # Visual Studio LightSwitch build output
262 | **/*.HTMLClient/GeneratedArtifacts
263 | **/*.DesktopClient/GeneratedArtifacts
264 | **/*.DesktopClient/ModelManifest.xml
265 | **/*.Server/GeneratedArtifacts
266 | **/*.Server/ModelManifest.xml
267 | _Pvt_Extensions
268 |
269 | # Paket dependency manager
270 | .paket/paket.exe
271 | paket-files/
272 |
273 | # FAKE - F# Make
274 | .fake/
275 |
276 | # JetBrains Rider
277 | .idea/
278 | *.sln.iml
279 |
280 | # CodeRush
281 | .cr/
282 |
283 | # Python Tools for Visual Studio (PTVS)
284 | __pycache__/
285 | *.pyc
286 |
287 | # Cake - Uncomment if you are using it
288 | # tools/**
289 | # !tools/packages.config
290 |
291 | # Telerik's JustMock configuration file
292 | *.jmconfig
293 |
294 | # BizTalk build output
295 | *.btp.cs
296 | *.btm.cs
297 | *.odx.cs
298 | *.xsd.cs
299 | coverage/coverage-final.json
300 | .nyc_output/
301 | boost.bat
302 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - "node"
5 | install:
6 | - npm install
7 | script:
8 | - npm test
9 | after_success:
10 | - npm run report-coverage
11 | deploy:
12 | skip_cleanup: true
13 | provider: npm
14 | email: "ivansanzcarasa@gmail.com"
15 | api_key: $NPM_TOKEN
16 | on:
17 | branch: master
18 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.renderWhitespace": "all",
3 | "editor.wordWrap": "on",
4 | "editor.renderControlCharacters": true,
5 | "typescript.format.placeOpenBraceOnNewLineForFunctions": true,
6 | "typescript.format.placeOpenBraceOnNewLineForControlBlocks": true,
7 | "javascript.format.placeOpenBraceOnNewLineForFunctions": true,
8 | "typescript.implementationsCodeLens.enabled": true,
9 | "editor.quickSuggestionsDelay": 0,
10 | "typescript.referencesCodeLens.enabled": true,
11 | "files.exclude": {
12 | "**/.git": true,
13 | "**/*.map": true,
14 | "**/node_modules": true,
15 | "**/.nyc_output": true,
16 | "**/coverage": true,
17 | "**/etc": true
18 | },
19 | "files.insertFinalNewline": true
20 | }
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Iván Sanz Carasa
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Linq-Collections: (IEnumerable, ...) + (List, Dictionary, ...)
2 |
3 | [](https://npmjs.org/package/linq-collections)
4 | [](https://npmjs.org/package/linq-collections)
5 | [](https://travis-ci.org/isc30/linq-collections)
6 | [](https://coveralls.io/github/isc30/linq-collections?branch=master)
7 | [](https://isc30.github.io/linq-collections/mocha)
8 |
9 |
10 | Strongly typed *Linq* implementation for *Javascript* and *TypeScript* (*ES5*, *ES6*, +)
11 | Includes collections (+ readonly versions): List, Dictionary, Stack, ...
12 |
13 | ## Current Stable Version
14 | https://github.com/isc30/linq-collections
15 | This project was developed by Ivan Sanz (isc30)
16 |
17 | The project is already finished, yet some features are missing. If you want to contribute with any of these, please check the [Development status and missing features list](https://github.com/isc30/linq-collections/projects/1)
18 | I will be happy to accept pull requests :D
19 |
20 | [](https://isc30.github.io/linq-collections/mocha)
21 |
22 | ## Intellisense friendly
23 | Every single method has **complete** type definitions available.
24 | If you use TypeScript, its purely is based in **generics**.
25 | [Insert motivational GIF with intellisense in action]
26 |
27 | ## Browser compatibility: 100%
28 | Using **ES5**, it has **100% compatibility** with nodejs and all main browsers (+mobile)
29 | Check your browser now if you don't believe it -> [](https://isc30.github.io/linq-collections/mocha)
30 |
31 | ## Performance
32 | *Linq-Collections* uses custom **iterators** and **deferred execution** mechanisms that ensure **BLAZING FAST** operations, outperforming any other popular library. Its also optimized to work with **minimal CPU and RAM usage**.
33 |
34 | ## Why use it?
35 | If previous reasons aren't enought, here are few more:
36 | - **Javascript && TypeScript compatible** - You can use it with JS or TypeScript (contains .d.ts definitions)
37 | - **No dependencies** - Pure and lightweight
38 | - **100% browser/nodejs support** - Stop caring about compatibility, it works everywhere!
39 | - **Strongly typed** - Developed in TypeScript, it uses no 'any' or dirty code. Everything is based in generics and strongly typed
40 | - **Best performance** - Deferred execution with custom iterators make the difference. Currently the fastest library.
41 | - **Works out of the box** - *'npm install linq-collections'* is the hardest thing you'll need to do
42 | - **Collections** - Provides many type of collections (list, dictionary, ... + readonly) with linq integrated inside. As in C#
43 | - **Strict standard** - Strictly implementing [microsoft's official linq definition](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/classification-of-standard-query-operators-by-manner-of-execution) (you can check it for exceptions, behavior, etc)
44 | - **Deeply tested** - Each new version is passing tons of quality tests before being released
45 |
46 | ## Using the package
47 | Interfaces for this library are already designed. New versions won't break any old code.
48 | We strongly recommend using `*` for version selector
49 | ```json
50 | dependencies {
51 | "linq-collections": "1.*"
52 | }
53 | ```
54 |
55 | ## Features
56 | Complete **Linq to Objects** implementation (deferred execution)
57 | > toArray, toList, toDictionary, toLookup, aggregate, all, any, average, concat, contains, count, defaultIfEmpty, distinct, elementAt, elementAtOrDefault, except, first, firstOrDefault, forEach, groupBy, groupJoin, intersect, join, last, lastOrDefault, longCount, max, min, orderBy, orderByDescending, reverse, select, selectMany, sequenceEquals, single, single, singleOrDefault, skip, skipWhile, sum, take, takeWhile, union, where, zip, ...
58 |
59 | Collections (+ readonly versions)
60 | > List, Dictionary, Stack, Queue, ...
61 |
62 | All Collections are **Queryable**
63 | ```typescript
64 | const list = new List([
65 | "Hello",
66 | "Bye",
67 | "Thanks",
68 | ]);
69 |
70 | const notHello = list.where(e => e !== "Hello");
71 | ```
72 |
73 | ## How to run tests
74 | This library uses `mocha` with custom assertion helper for testing.
75 | Use `nyc mocha` to run the tests and coverage.
76 |
77 | ## Hall of fame
78 | * [@nikolalukovic](https://github.com/nikolalukovic)
79 | * [@tholdrim](https://github.com/tholdrim)
80 |
--------------------------------------------------------------------------------
/mocha.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mocha Tests
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "linq-collections",
3 | "version": "1.0.255",
4 | "description": "Linq-Collections (ES5): [IEnumerable, IQueryable, ...] + [List, Dictionary, Stack, ... + readonly]",
5 | "main": "./build/src/Linq.js",
6 | "files": [
7 | "build/src",
8 | "README.md",
9 | "LICENSE"
10 | ],
11 | "typescript": {
12 | "definition": "./build/src/Linq.d.ts"
13 | },
14 | "typings": "./build/src/Linq.d.ts",
15 | "types": "./build/src/Linq.d.ts",
16 | "author": {
17 | "name": "Ivan Sanz",
18 | "email": "ivansanzcarasa@gmail.com",
19 | "url": "https://github.com/isc30"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/isc30/linq-collections/issues"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/isc30/linq-collections"
27 | },
28 | "homepage": "https://github.com/isc30/linq-collections#readme",
29 | "scripts": {
30 | "node-compile": "tsc",
31 | "web-tests-compile": "browserify ./test/TestSuite.ts -p [tsify] > linq-tests.web.js",
32 | "pretest": "npm run node-compile && npm run web-tests-compile",
33 | "test": "nyc mocha ./build/test/TestSuite.js --slow 0",
34 | "report-coverage": "nyc report --reporter=text-lcov | coveralls",
35 | "publish-node": "npm run node-compile",
36 | "publish-web": "browserify ./src/Linq.ts -p [tsify] > ./build/src/linq.web.js",
37 | "prepublish": "npm run node-compile"
38 | },
39 | "dependencies": {},
40 | "devDependencies": {
41 | "typescript": "*",
42 | "@types/mocha": "*",
43 | "mocha": "*",
44 | "coveralls": "*",
45 | "nyc": "*",
46 | "browserify": "*",
47 | "tsify": "*"
48 | },
49 | "directories": {
50 | "test": "test",
51 | "lib": "src"
52 | },
53 | "license": "MIT",
54 | "keywords": [
55 | "linq",
56 | ".net",
57 | "dotnet",
58 | "c#",
59 | "csharp",
60 | "visualbasic",
61 | "collections",
62 | "enumerable",
63 | "ienumerable",
64 | "ts",
65 | "typescript",
66 | "js",
67 | "javascript",
68 | "dictionary",
69 | "idictionary",
70 | "list",
71 | "ilist",
72 | "container",
73 | "hashset",
74 | "objects",
75 | "csharp",
76 | "map",
77 | "iterator",
78 | "iterators",
79 | "array",
80 | "readonly",
81 | "read-only",
82 | "readonlyList",
83 | "readonlydictionary",
84 | "mobile",
85 | "compatible",
86 | "es5",
87 | "ecmascript5",
88 | "es6"
89 | ],
90 | "nyc": {
91 | "exclude": [
92 | "build/test"
93 | ]
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/Collections.ts:
--------------------------------------------------------------------------------
1 | // -
2 | // Created by Ivan Sanz (@isc30)
3 | // Copyright © 2017 Ivan Sanz Carasa. All rights reserved.
4 | // -
5 |
6 | // region IMPORTS
7 | // tslint:disable-next-line:max-line-length
8 |
9 | import { Action, Aggregator, Dynamic, Indexer, Predicate, Selector, ZipSelector, Type } from "./Types";
10 | import
11 | {
12 | ArrayEnumerable,
13 | ConcatEnumerable,
14 | ConditionalEnumerable,
15 | Enumerable,
16 | IEnumerable,
17 | IGrouping,
18 | IKeyValue,
19 | IOrderedEnumerable,
20 | IQueryable,
21 | OrderedEnumerable,
22 | RangeEnumerable,
23 | ReverseEnumerable,
24 | TransformEnumerable,
25 | UniqueEnumerable,
26 | ZippedEnumerable,
27 | } from "./Enumerables";
28 | import { Comparer, EqualityComparer, strictEqualityComparer, createComparer } from "./Comparers";
29 |
30 | import { IIterable, ArrayIterator } from "./Iterators";
31 |
32 | // endregion
33 |
34 | // region EnumerableCollection
35 | export abstract class EnumerableCollection
36 | implements IQueryable
37 | {
38 | public abstract copy(): IQueryable;
39 | public abstract asEnumerable(): IEnumerable;
40 | public abstract toArray(): TElement[];
41 |
42 | public toList(): IList
43 | {
44 | return new List(this.toArray());
45 | }
46 |
47 | public toDictionary(
48 | keySelector: Selector,
49 | valueSelector: Selector)
50 | : IDictionary
51 | {
52 | return Dictionary.fromArray(this.toArray(), keySelector, valueSelector);
53 | }
54 |
55 | public reverse(): IEnumerable
56 | {
57 | return new ReverseEnumerable(this.asEnumerable());
58 | }
59 |
60 | public concat(
61 | other: TElement[] | IQueryable,
62 | ...others: Array>)
63 | : IEnumerable
64 | {
65 | return this.asEnumerable().concat(other, ...others);
66 | }
67 |
68 | public contains(element: TElement): boolean
69 | {
70 | return this.any(e => e === element);
71 | }
72 |
73 | public where(predicate: Predicate): IEnumerable
74 | {
75 | return new ConditionalEnumerable(this.asEnumerable(), predicate);
76 | }
77 |
78 | public select(selector: Selector): IEnumerable
79 | {
80 | return new TransformEnumerable(this.asEnumerable(), selector);
81 | }
82 |
83 | public selectMany(
84 | selector: Selector | IEnumerable>)
85 | : IEnumerable
86 | {
87 | const selectToEnumerable = (e: TElement) =>
88 | {
89 | const ie = selector(e);
90 |
91 | return ie instanceof Array
92 | ? new ArrayEnumerable(ie)
93 | : ie.asEnumerable();
94 | };
95 |
96 | return this
97 | .select(selectToEnumerable).toArray()
98 | .reduce((p, c) => new ConcatEnumerable(p, c), Enumerable.empty()) as IEnumerable;
99 | }
100 |
101 | public elementAt(index: number): TElement
102 | {
103 | const element = this.elementAtOrDefault(index);
104 |
105 | if (element === undefined)
106 | {
107 | throw new Error("Out of bounds");
108 | }
109 |
110 | return element;
111 | }
112 |
113 | public except(other: IQueryable): IEnumerable
114 | {
115 | return this.asEnumerable().except(other);
116 | }
117 |
118 | public first(): TElement;
119 | public first(predicate: Predicate): TElement;
120 | public first(predicate?: Predicate): TElement
121 | {
122 | let element: TElement | undefined;
123 |
124 | if (predicate !== undefined)
125 | {
126 | element = this.firstOrDefault(predicate);
127 | }
128 | else
129 | {
130 | element = this.firstOrDefault();
131 | }
132 |
133 | if (element === undefined)
134 | {
135 | throw new Error("Sequence contains no elements");
136 | }
137 |
138 | return element;
139 | }
140 |
141 | public groupBy(
142 | keySelector: Selector)
143 | : IEnumerable>;
144 | public groupBy(
145 | keySelector: Selector,
146 | valueSelector: Selector)
147 | : IEnumerable>;
148 | public groupBy(
149 | keySelector: Selector,
150 | valueSelector?: Selector)
151 | : IEnumerable>
152 | {
153 | const array = this.toArray();
154 | const dictionary = new Dictionary>();
155 |
156 | for (let i = 0; i < array.length; ++i)
157 | {
158 | const key = keySelector(array[i]);
159 | const value = valueSelector !== undefined
160 | ? valueSelector(array[i])
161 | : array[i];
162 |
163 | if (!dictionary.containsKey(key))
164 | {
165 | dictionary.set(key, new List());
166 | }
167 |
168 | (dictionary.get(key) as IList).push(value);
169 | }
170 |
171 | return dictionary.asEnumerable();
172 | }
173 |
174 | public last(): TElement;
175 | public last(predicate: Predicate): TElement;
176 | public last(predicate?: Predicate): TElement
177 | {
178 | let element: TElement | undefined;
179 |
180 | if (predicate !== undefined)
181 | {
182 | element = this.lastOrDefault(predicate);
183 | }
184 | else
185 | {
186 | element = this.lastOrDefault();
187 | }
188 |
189 | if (element === undefined)
190 | {
191 | throw new Error("Sequence contains no elements");
192 | }
193 |
194 | return element;
195 | }
196 |
197 | public single(): TElement;
198 | public single(predicate: Predicate): TElement;
199 | public single(predicate?: Predicate): TElement
200 | {
201 | let element: TElement | undefined;
202 |
203 | if (predicate !== undefined)
204 | {
205 | element = this.singleOrDefault(predicate);
206 | }
207 | else
208 | {
209 | element = this.singleOrDefault();
210 | }
211 |
212 | if (element === undefined)
213 | {
214 | throw new Error("Sequence contains no elements");
215 | }
216 |
217 | return element;
218 | }
219 |
220 | public singleOrDefault(): TElement | undefined;
221 | public singleOrDefault(predicate: Predicate): TElement | undefined;
222 | public singleOrDefault(predicate?: Predicate): TElement | undefined
223 | {
224 | if (predicate !== undefined)
225 | {
226 | return this.asEnumerable().singleOrDefault(predicate);
227 | }
228 |
229 | return this.asEnumerable().singleOrDefault();
230 | }
231 |
232 | public skipWhile(predicate: Predicate): IEnumerable
233 | {
234 | return this.asEnumerable().skipWhile(predicate);
235 | }
236 |
237 | public takeWhile(predicate: Predicate): IEnumerable
238 | {
239 | return this.asEnumerable().takeWhile(predicate);
240 | }
241 |
242 | public sequenceEqual(other: IQueryable | TElement[]): boolean
243 | public sequenceEqual(other: IQueryable | TElement[], comparer: EqualityComparer): boolean;
244 | public sequenceEqual(other: IQueryable | TElement[], comparer?: EqualityComparer): boolean
245 | {
246 | if (comparer !== undefined)
247 | {
248 | return this.asEnumerable().sequenceEqual(other, comparer);
249 | }
250 |
251 | return this.asEnumerable().sequenceEqual(other);
252 | }
253 |
254 | public distinct(): IEnumerable;
255 | public distinct(keySelector: Selector): IEnumerable;
256 | public distinct(keySelector?: Selector): IEnumerable
257 | {
258 | return new UniqueEnumerable(this.asEnumerable(), keySelector);
259 | }
260 |
261 | public min(): TElement;
262 | public min(selector: Selector): TSelectorOut;
263 | public min(selector?: Selector): TElement | TSelectorOut
264 | {
265 | if (selector !== undefined)
266 | {
267 | // Don't copy iterators
268 | return new TransformEnumerable(this.asEnumerable(), selector).min();
269 | }
270 |
271 | return this.aggregate((previous, current) =>
272 | (previous !== undefined && previous < current)
273 | ? previous
274 | : current);
275 | }
276 |
277 | public orderBy(
278 | keySelector: Selector): IOrderedEnumerable;
279 | public orderBy(
280 | keySelector: Selector,
281 | comparer: Comparer): IOrderedEnumerable;
282 | public orderBy(
283 | keySelector: Selector,
284 | comparer?: Comparer): IOrderedEnumerable
285 | {
286 | return new OrderedEnumerable(this.asEnumerable(), createComparer(keySelector, true, comparer));
287 | }
288 |
289 | public orderByDescending(
290 | keySelector: Selector): IOrderedEnumerable
291 | {
292 | return new OrderedEnumerable(this.asEnumerable(), createComparer(keySelector, false, undefined));
293 | }
294 |
295 | public max(): TElement;
296 | public max(selector: Selector): TSelectorOut;
297 | public max(selector?: Selector): TElement | TSelectorOut
298 | {
299 | if (selector !== undefined)
300 | {
301 | // Don't copy iterators
302 | return new TransformEnumerable(this.asEnumerable(), selector).max();
303 | }
304 |
305 | return this.aggregate((previous, current) =>
306 | (previous !== undefined && previous > current)
307 | ? previous
308 | : current);
309 | }
310 |
311 | public sum(selector: Selector): number
312 | {
313 | return this.aggregate(
314 | (previous: number, current: TElement) => previous + selector(current), 0);
315 | }
316 |
317 | public skip(amount: number): IEnumerable
318 | {
319 | return new RangeEnumerable(this.asEnumerable(), amount, undefined);
320 | }
321 |
322 | public take(amount: number): IEnumerable
323 | {
324 | return new RangeEnumerable(this.asEnumerable(), undefined, amount);
325 | }
326 |
327 | public union(other: IQueryable): IEnumerable
328 | {
329 | return new UniqueEnumerable(this.concat(other));
330 | }
331 |
332 | public aggregate(aggregator: Aggregator): TElement;
333 | public aggregate(aggregator: Aggregator, initialValue: TValue): TValue;
334 | public aggregate(
335 | aggregator: Aggregator,
336 | initialValue?: TValue): TValue | TElement
337 | {
338 | if (initialValue !== undefined)
339 | {
340 | return this.asEnumerable().aggregate(
341 | aggregator as Aggregator,
342 | initialValue);
343 | }
344 |
345 | return this.asEnumerable().aggregate(
346 | aggregator as Aggregator);
347 | }
348 |
349 | public any(): boolean;
350 | public any(predicate: Predicate): boolean;
351 | public any(predicate?: Predicate): boolean
352 | {
353 | if (predicate !== undefined)
354 | {
355 | return this.asEnumerable().any(predicate);
356 | }
357 |
358 | return this.asEnumerable().any();
359 | }
360 |
361 | public all(predicate: Predicate): boolean
362 | {
363 | return this.asEnumerable().all(predicate);
364 | }
365 |
366 | public average(selector: Selector): number
367 | {
368 | return this.asEnumerable().average(selector);
369 | }
370 |
371 | public count(): number;
372 | public count(predicate: Predicate): number;
373 | public count(predicate?: Predicate): number
374 | {
375 | if (predicate !== undefined)
376 | {
377 | return this.asEnumerable().count(predicate);
378 | }
379 |
380 | return this.asEnumerable().count();
381 | }
382 |
383 | public elementAtOrDefault(index: number): TElement | undefined
384 | {
385 | return this.asEnumerable().elementAtOrDefault(index);
386 | }
387 |
388 | public firstOrDefault(): TElement | undefined;
389 | public firstOrDefault(predicate: Predicate): TElement | undefined;
390 | public firstOrDefault(predicate?: Predicate): TElement | undefined
391 | {
392 | if (predicate !== undefined)
393 | {
394 | return this.asEnumerable().firstOrDefault(predicate);
395 | }
396 |
397 | return this.asEnumerable().firstOrDefault();
398 | }
399 |
400 | public lastOrDefault(): TElement | undefined;
401 | public lastOrDefault(predicate: Predicate): TElement | undefined;
402 | public lastOrDefault(predicate?: Predicate): TElement | undefined
403 | {
404 | if (predicate !== undefined)
405 | {
406 | return this.asEnumerable().lastOrDefault(predicate);
407 | }
408 |
409 | return this.asEnumerable().lastOrDefault();
410 | }
411 |
412 | public forEach(action: Action): void
413 | {
414 | return this.asEnumerable().forEach(action);
415 | }
416 |
417 | public defaultIfEmpty(): IEnumerable;
418 | public defaultIfEmpty(defaultValue: TElement): IEnumerable;
419 | public defaultIfEmpty(defaultValue?: TElement): IEnumerable
420 | {
421 | if (defaultValue !== undefined)
422 | {
423 | return this.asEnumerable().defaultIfEmpty(defaultValue);
424 | }
425 |
426 | return this.asEnumerable().defaultIfEmpty();
427 | }
428 |
429 | public zip(other: IQueryable | TOther[], selector: ZipSelector): IEnumerable
430 | {
431 | return this.asEnumerable().zip(other, selector);
432 | }
433 | }
434 | // endregion
435 | // region ArrayQueryable
436 | export abstract class ArrayQueryable
437 | extends EnumerableCollection
438 | {
439 | protected source: TElement[];
440 |
441 | public abstract copy(): IQueryable;
442 |
443 | public constructor();
444 | public constructor(elements: TElement[])
445 | public constructor(elements: TElement[] = [])
446 | {
447 | super();
448 | this.source = elements;
449 | }
450 |
451 | public asArray(): TElement[]
452 | {
453 | return this.source;
454 | }
455 |
456 | public toArray(): TElement[]
457 | {
458 | return ([] as TElement[]).concat(this.source);
459 | }
460 |
461 | public toList(): IList
462 | {
463 | return new List(this.toArray());
464 | }
465 |
466 | public asEnumerable(): IEnumerable
467 | {
468 | return new ArrayEnumerable(this.source);
469 | }
470 |
471 | public aggregate(aggregator: Aggregator): TElement;
472 | public aggregate(aggregator: Aggregator, initialValue: TValue): TValue;
473 | public aggregate(
474 | aggregator: Aggregator,
475 | initialValue?: TValue): TValue | TElement
476 | {
477 | if (initialValue !== undefined)
478 | {
479 | return this.source.reduce(
480 | aggregator as Aggregator,
481 | initialValue);
482 | }
483 |
484 | return this.source.reduce(aggregator as Aggregator);
485 | }
486 |
487 | public any(): boolean;
488 | public any(predicate: Predicate): boolean;
489 | public any(predicate?: Predicate): boolean
490 | {
491 | if (predicate !== undefined)
492 | {
493 | return this.source.some(predicate);
494 | }
495 |
496 | return this.source.length > 0;
497 | }
498 |
499 | public all(predicate: Predicate): boolean
500 | {
501 | return this.source.every(predicate);
502 | }
503 |
504 | public average(selector: Selector): number
505 | {
506 | if (this.count() === 0)
507 | {
508 | throw new Error("Sequence contains no elements");
509 | }
510 |
511 | let sum = 0;
512 |
513 | for (let i = 0, end = this.source.length; i < end; ++i)
514 | {
515 | sum += selector(this.source[i]);
516 | }
517 |
518 | return sum / this.source.length;
519 | }
520 |
521 | public count(): number;
522 | public count(predicate: Predicate): number;
523 | public count(predicate?: Predicate): number
524 | {
525 | if (predicate !== undefined)
526 | {
527 | return this.source.filter(predicate).length;
528 | }
529 |
530 | return this.source.length;
531 | }
532 |
533 | public elementAtOrDefault(index: number): TElement | undefined
534 | {
535 | if (index < 0)
536 | {
537 | throw new Error("Negative index is forbiden");
538 | }
539 |
540 | return this.source[index];
541 | }
542 |
543 | public firstOrDefault(): TElement | undefined;
544 | public firstOrDefault(predicate: Predicate): TElement | undefined;
545 | public firstOrDefault(predicate?: Predicate): TElement | undefined
546 | {
547 | if (predicate !== undefined)
548 | {
549 | return this.source.filter(predicate)[0];
550 | }
551 |
552 | return this.source[0];
553 | }
554 |
555 | public groupBy(
556 | keySelector: Selector)
557 | : IEnumerable>;
558 | public groupBy(
559 | keySelector: Selector,
560 | valueSelector: Selector)
561 | : IEnumerable>;
562 | public groupBy(
563 | keySelector: Selector,
564 | valueSelector?: Selector)
565 | : IEnumerable>
566 | {
567 | const array = this.asArray();
568 | const dictionary = new Dictionary>();
569 |
570 | for (let i = 0; i < array.length; ++i)
571 | {
572 | const key = keySelector(array[i]);
573 | const value = valueSelector !== undefined
574 | ? valueSelector(array[i])
575 | : array[i];
576 |
577 | if (!dictionary.containsKey(key))
578 | {
579 | dictionary.set(key, new List());
580 | }
581 |
582 | (dictionary.get(key) as IList).push(value);
583 | }
584 |
585 | return dictionary.asEnumerable();
586 | }
587 |
588 | public lastOrDefault(): TElement | undefined;
589 | public lastOrDefault(predicate: Predicate): TElement | undefined;
590 | public lastOrDefault(predicate?: Predicate): TElement | undefined
591 | {
592 | if (predicate !== undefined)
593 | {
594 | const records = this.source.filter(predicate);
595 |
596 | return records[records.length - 1];
597 | }
598 |
599 | return this.source[this.source.length - 1];
600 | }
601 |
602 | public forEach(action: Action): void
603 | {
604 | for (let i = 0, end = this.source.length; i < end; ++i)
605 | {
606 | action(this.source[i], i);
607 | }
608 | }
609 |
610 | public sequenceEqual(other: IQueryable | TElement[]): boolean;
611 | public sequenceEqual(other: IQueryable | TElement[], comparer: EqualityComparer): boolean;
612 | public sequenceEqual(other: IQueryable | TElement[], comparer: EqualityComparer = strictEqualityComparer()): boolean
613 | {
614 | if (other instanceof ArrayQueryable
615 | || other instanceof Array)
616 | {
617 | const thisArray = this.asArray();
618 | const otherArray = other instanceof ArrayQueryable
619 | ? other.asArray() as TElement[]
620 | : other;
621 |
622 | if (thisArray.length != otherArray.length)
623 | {
624 | return false;
625 | }
626 |
627 | for (let i = 0; i < thisArray.length; ++i)
628 | {
629 | if (!comparer(thisArray[i], otherArray[i]))
630 | {
631 | return false;
632 | }
633 | }
634 |
635 | return true;
636 | }
637 |
638 | return this.asEnumerable().sequenceEqual(other, comparer);
639 | }
640 | }
641 | // endregion
642 | // region List
643 | export interface IReadOnlyList
644 | extends IQueryable
645 | {
646 | copy(): IList;
647 |
648 | get(index: number): TElement | undefined;
649 |
650 | indexOf(element: TElement): number;
651 | }
652 |
653 | export interface IList
654 | extends IReadOnlyList
655 | {
656 | asReadOnly(): IReadOnlyList;
657 | asArray(): TElement[];
658 | clear(): void;
659 | push(element: TElement): number;
660 | pushRange(elements: TElement[] | IQueryable): number;
661 | pushFront(element: TElement): number;
662 | pop(): TElement | undefined;
663 | popFront(): TElement | undefined;
664 | remove(element: TElement): void;
665 | removeAt(index: number): TElement | undefined;
666 | set(index: number, element: TElement): void;
667 | insert(index: number, element: TElement): void;
668 | }
669 |
670 | export class List
671 | extends ArrayQueryable
672 | implements IList
673 | {
674 | public copy(): IList
675 | {
676 | return new List(this.toArray());
677 | }
678 |
679 | public asReadOnly(): IReadOnlyList
680 | {
681 | return this;
682 | }
683 |
684 | public clear(): void
685 | {
686 | this.source = [];
687 | }
688 |
689 | public remove(element: TElement): void
690 | {
691 | const newSource: TElement[] = [];
692 |
693 | for (let i = 0, end = this.source.length; i < end; ++i)
694 | {
695 | if (this.source[i] !== element)
696 | {
697 | newSource.push(this.source[i]);
698 | }
699 | }
700 |
701 | this.source = newSource;
702 | }
703 |
704 | public removeAt(index: number): TElement | undefined
705 | {
706 | if (index < 0 || this.source[index] === undefined)
707 | {
708 | throw new Error("Out of bounds");
709 | }
710 |
711 | return this.source.splice(index, 1)[0];
712 | }
713 |
714 | public get(index: number): TElement | undefined
715 | {
716 | return this.source[index];
717 | }
718 |
719 | public push(element: TElement): number
720 | {
721 | return this.source.push(element);
722 | }
723 |
724 | public pushRange(elements: TElement[] | IQueryable): number
725 | {
726 | if (!(elements instanceof Array))
727 | {
728 | elements = elements.toArray();
729 | }
730 |
731 | return this.source.push.apply(this.source, elements);
732 | }
733 |
734 | public pushFront(element: TElement): number
735 | {
736 | return this.source.unshift(element);
737 | }
738 |
739 | public pop(): TElement | undefined
740 | {
741 | return this.source.pop();
742 | }
743 |
744 | public popFront(): TElement | undefined
745 | {
746 | return this.source.shift();
747 | }
748 |
749 | public set(index: number, element: TElement): void
750 | {
751 | if (index < 0)
752 | {
753 | throw new Error("Out of bounds");
754 | }
755 |
756 | this.source[index] = element;
757 | }
758 |
759 | public insert(index: number, element: TElement): void
760 | {
761 | if (index < 0 || index > this.source.length)
762 | {
763 | throw new Error("Out of bounds");
764 | }
765 |
766 | this.source.splice(index, 0, element);
767 | }
768 |
769 | public indexOf(element: TElement): number
770 | {
771 | return this.source.indexOf(element);
772 | }
773 | }
774 | // endregion
775 | // region Stack
776 | export interface IStack
777 | extends IQueryable
778 | {
779 | copy(): IStack;
780 |
781 | asArray(): TElement[];
782 | clear(): void;
783 | peek(): TElement | undefined;
784 | pop(): TElement | undefined;
785 | push(element: TElement): number;
786 | }
787 |
788 | export class Stack
789 | extends ArrayQueryable
790 | implements IStack
791 | {
792 | public copy(): IStack
793 | {
794 | return new Stack(this.toArray());
795 | }
796 |
797 | public clear(): void
798 | {
799 | this.source = [];
800 | }
801 |
802 | public peek(): TElement | undefined
803 | {
804 | return this.source[this.source.length - 1];
805 | }
806 |
807 | public pop(): TElement | undefined
808 | {
809 | return this.source.pop();
810 | }
811 |
812 | public push(element: TElement): number
813 | {
814 | return this.source.push(element);
815 | }
816 | }
817 | // endregion
818 | // region Dictionary
819 | export interface IReadOnlyDictionary
820 | extends IQueryable>
821 | {
822 | copy(): IDictionary;
823 |
824 | containsKey(key: TKey): boolean;
825 | containsValue(value: TValue): boolean;
826 | getKeys(): IList;
827 | getValues(): IList;
828 |
829 | get(key: TKey): TValue;
830 | }
831 |
832 | export interface IDictionary
833 | extends IReadOnlyDictionary
834 | {
835 | asReadOnly(): IReadOnlyDictionary;
836 | clear(): void;
837 | remove(key: TKey): void;
838 | set(key: TKey, value: TValue): void;
839 | setOrUpdate(key: TKey, value: TValue): void;
840 | }
841 |
842 | export class Dictionary
843 | extends EnumerableCollection>
844 | implements IDictionary
845 | {
846 | public static fromArray(
847 | array: TArray[],
848 | keySelector: Selector,
849 | valueSelector: Selector)
850 | : IDictionary
851 | {
852 | const keyValuePairs = array.map>(v =>
853 | {
854 | return {
855 | key: keySelector(v),
856 | value: valueSelector(v),
857 | };
858 | });
859 |
860 | return new Dictionary(keyValuePairs);
861 | }
862 |
863 | public static fromJsObject(
864 | object: Dynamic)
865 | : IDictionary
866 | {
867 | const keys = new List(Object.getOwnPropertyNames(object));
868 | const keyValues = keys.select(k => >{ key: k, value: object[k] });
869 |
870 | return new Dictionary(keyValues.toArray());
871 | }
872 |
873 | protected dictionary: Dynamic;
874 | protected keyType: Type;
875 |
876 | public constructor();
877 | public constructor(keyValuePairs: Array>);
878 | public constructor(keyValuePairs?: Array>)
879 | {
880 | super();
881 | this.clear();
882 |
883 | if (keyValuePairs !== undefined)
884 | {
885 | for (let i = 0; i < keyValuePairs.length; ++i)
886 | {
887 | const pair = keyValuePairs[i];
888 | this.set(pair.key, pair.value);
889 | }
890 | }
891 | }
892 |
893 | public copy(): IDictionary
894 | {
895 | return new Dictionary(this.toArray());
896 | }
897 |
898 | public asReadOnly(): IReadOnlyDictionary
899 | {
900 | return this;
901 | }
902 |
903 | public asEnumerable(): IEnumerable>
904 | {
905 | return new ArrayEnumerable(this.toArray());
906 | }
907 |
908 | public toArray(): Array>
909 | {
910 | return this.getKeys().select>(p =>
911 | {
912 | return {
913 | key: p,
914 | value: this.dictionary[p],
915 | };
916 | }).toArray();
917 | }
918 |
919 | public clear(): void
920 | {
921 | this.dictionary = {};
922 | }
923 |
924 | public containsKey(key: TKey): boolean
925 | {
926 | return this.dictionary.hasOwnProperty(key);
927 | }
928 |
929 | public containsValue(value: TValue): boolean
930 | {
931 | const keys = this.getKeysFast();
932 |
933 | for (let i = 0; i < keys.length; ++i)
934 | {
935 | if (this.dictionary[keys[i]] === value)
936 | {
937 | return true;
938 | }
939 | }
940 |
941 | return false;
942 | }
943 |
944 | public getKeys(): IList
945 | {
946 | const keys = this.getKeysFast();
947 |
948 | return new List(keys.map(
949 | k => this.keyType === "number"
950 | ? parseFloat(k)
951 | : k) as TKey[]);
952 | }
953 |
954 | protected getKeysFast(): string[]
955 | {
956 | return Object.getOwnPropertyNames(this.dictionary);
957 | }
958 |
959 | public getValues(): IList
960 | {
961 | const keys = this.getKeysFast();
962 | const result = new Array(keys.length);
963 |
964 | for (let i = 0; i < keys.length; ++i)
965 | {
966 | result[i] = this.dictionary[keys[i]];
967 | }
968 |
969 | return new List(result);
970 | }
971 |
972 | public remove(key: TKey): void
973 | {
974 | if (this.containsKey(key))
975 | {
976 | delete this.dictionary[key];
977 | }
978 | }
979 |
980 | public get(key: TKey): TValue
981 | {
982 | if (!this.containsKey(key))
983 | {
984 | throw new Error(`Key doesn't exist: ${key}`)
985 | }
986 |
987 | return this.dictionary[key];
988 | }
989 |
990 | public set(key: TKey, value: TValue): void
991 | {
992 | if (this.containsKey(key))
993 | {
994 | throw new Error(`Key already exists: ${key}`);
995 | }
996 |
997 | this.setOrUpdate(key, value);
998 | }
999 |
1000 | public setOrUpdate(key: TKey, value: TValue): void
1001 | {
1002 | if (this.keyType === undefined)
1003 | {
1004 | this.keyType = typeof key;
1005 | }
1006 |
1007 | this.dictionary[key] = value;
1008 | }
1009 | }
1010 | // endregion
1011 |
--------------------------------------------------------------------------------
/src/Comparers.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Created by Ivan Sanz (@isc30)
3 | * Copyright © 2017 Ivan Sanz Carasa. All rights reserved.
4 | */
5 |
6 | import { Selector } from "./Types";
7 |
8 | export type ComparerResult = -1 | 0 | 1;
9 | export type Comparer = (left: T, right: T) => ComparerResult;
10 |
11 | export type EqualityComparer = (left: T, right: T) => boolean;
12 | export const strictEqualityComparer = () => (left: T, right: T) => left === right;
13 |
14 | export function combineComparers(left: Comparer, right: Comparer): Comparer
15 | {
16 | return (l: T, r: T) => left(l, r) || right(l, r);
17 | }
18 |
19 | export function createComparer(
20 | keySelector: Selector,
21 | ascending: boolean,
22 | customComparer?: Comparer): Comparer
23 | {
24 | if (customComparer !== undefined)
25 | {
26 | return (l: TElement, r: TElement) => customComparer(keySelector(l), keySelector(r));
27 | }
28 |
29 | return ascending
30 | ? (l: TElement, r: TElement) =>
31 | {
32 | const left = keySelector(l);
33 | const right = keySelector(r);
34 |
35 | return left < right
36 | ? -1
37 | : left > right
38 | ? 1
39 | : 0;
40 | }
41 | : (l: TElement, r: TElement) =>
42 | {
43 | const left = keySelector(l);
44 | const right = keySelector(r);
45 |
46 | return left < right
47 | ? 1
48 | : left > right
49 | ? -1
50 | : 0;
51 | };
52 | }
53 |
--------------------------------------------------------------------------------
/src/Enumerables.ts:
--------------------------------------------------------------------------------
1 | // -
2 | // Created by Ivan Sanz (@isc30)
3 | // Copyright © 2017 Ivan Sanz Carasa. All rights reserved.
4 | // -
5 |
6 | // region IMPORTS
7 |
8 | import { Action, Aggregator, Dynamic, Indexer, Predicate, Selector, ZipSelector } from "./Types";
9 | import { ArrayIterator, IIterable } from "./Iterators";
10 | import { Comparer, EqualityComparer, strictEqualityComparer, combineComparers, createComparer } from "./Comparers";
11 | import { Dictionary, IDictionary, IList, List } from "./Collections";
12 |
13 | import { Cached } from "./Utils";
14 |
15 | // endregion
16 |
17 | // region Interfaces
18 | export interface IKeyValue
19 | {
20 | key: TKey;
21 | value: TValue;
22 | }
23 |
24 | export type IGrouping = IKeyValue>;
25 |
26 | export interface IQueryable
27 | {
28 | copy(): IQueryable;
29 |
30 | asEnumerable(): IEnumerable;
31 | toArray(): TOut[];
32 | toList(): IList;
33 | toDictionary(
34 | keySelector: Selector,
35 | valueSelector: Selector)
36 | : IDictionary;
37 | // toLookup
38 |
39 | aggregate(aggregator: Aggregator): TOut;
40 | aggregate(aggregator: Aggregator, initialValue: TValue): TValue;
41 |
42 | all(predicate: Predicate): boolean;
43 |
44 | any(): boolean;
45 | any(predicate: Predicate): boolean;
46 |
47 | average(selector: Selector): number;
48 |
49 | concat(
50 | other: TOut[] | IQueryable,
51 | ...others: Array>)
52 | : IEnumerable;
53 |
54 | contains(element: TOut): boolean;
55 |
56 | count(): number;
57 | count(predicate: Predicate): number;
58 |
59 | defaultIfEmpty(): IEnumerable;
60 | defaultIfEmpty(defaultValue: TOut): IEnumerable;
61 |
62 | distinct(): IEnumerable