diff --git a/.vscode/launch.json b/.vscode/launch.json index 578dafe..9d879dc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "type": "node", "request": "launch", "name": "Launch Program", - "program": "${workspaceFolder}\\demo\\typescript\\index.js", + "program": "${workspaceFolder}\\dist\\demo\\index.js", "outFiles": [ "${workspaceFolder}/**/*.js" ] diff --git a/README.md b/README.md index 5ea59f4..37c2d7a 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ asyncData.then(data => { - [x] selectMany - [x] join - [x] leftJoin -- [ ] groupJoin +- [x] groupJoin - [x] orderBy - [x] orderByDescending - [x] take @@ -125,7 +125,7 @@ asyncData.then(data => { - [x] skip - [x] skipWhile - [x] groupBy -- [ ] distinct +- [x] distinct - [ ] concat - [ ] zip - [ ] union @@ -137,15 +137,10 @@ asyncData.then(data => { - [x] lastOrDefault : `Promise` - [x] single - [x] singleOrDefault -- [ ] elementAt -- [ ] elementAtOrDefault -- [ ] orderByDescending -- [ ] defaultIfEmpty - [x] contains -- [ ] reserve - [ ] sequenceEqual - [x] any : `Promise` -- [ ] all +- [x] all : `Promise` - [x] count : `Promise` - [x] min : `Promise` - [x] max : `Promise` diff --git a/src/demo/index.ts b/src/demo/index.ts new file mode 100644 index 0000000..26ba006 --- /dev/null +++ b/src/demo/index.ts @@ -0,0 +1,91 @@ +import { Queryable } from '../implements'; +const cTable = require('console.table'); + +let players = new Promise<{ name, overall, nationId, skills }[]>((resolve, reject) => { + // skills: attack, stamia, speed, shoot + console.log('get players...'); + setTimeout(() => { + resolve([ + { name: 'Ronaldo', overall: 96, nationId: 1, skills: [96, 85, 87, 91] }, + { name: 'Messi', overall: 98, nationId: 2, skills: [97, 85, 91, 93] }, + { name: 'Mbappe', overall: 86, nationId: 3, skills: [89, 81, 95, 83] }, + { name: 'Matial', overall: 81, nationId: 3, skills: [81, 81, 89, 81] }, + { name: 'Salah', overall: 89, nationId: 4, skills: [88, 82, 97, 86] } + ]); + }, 1000); +}) + +let nations = new Promise<{ id, name, areaId }[]>(resolve => { + console.log('get nations...'); + setTimeout(() => { + resolve([ + { id: 1, name: 'Portugal', areaId: 1 }, + { id: 2, name: 'Argentina', areaId: 2 }, + { id: 3, name: 'France', areaId: 1 }, + { id: 4, name: 'Egypt', areaId: 3 } + ]); + }, 2000); +}) + +let continents = new Promise<{ id, areaName }[]>(resolve => { + console.log('get continents...'); + setTimeout(() => { + resolve([ + { id: 1, areaName: 'Euro' }, + { id: 2, areaName: 'South America' }, + ]); + }, 2300); +}) + +function main() { + let query10 = Queryable + .from(players) + .selectMany(x => x.skills) + .distinct(); + + query10.toList().then(d => console.table(d)); + + let query9 = Queryable + .from(players) + .concat(players); + + query9.toList().then(d => console.table(d)); + + let query = Queryable + .from(players) + .where(x => x.overall > 85) + .select(x => { + return { + name: x.name, + stars: (() => { x.overall > 90 ? 5 : 4 })(), + realOverall: Queryable.fromSync(x.skills).avarage(), + nationId: x.nationId + } + }) + .join(nations, (x, y) => x.nationId == y.id) + .leftJoin(continents, (plNation, con) => plNation.y.areaId == con.id) + .select(x => { + return { + name: x.x.name, + stars: x.x.stars, + realOverall: x.x.realOverall, + nation: x.y.name, + area: x.areaName + } + }) + .groupBy(x => x.area) + .select(x => { + return { + area: x.key, + avg: (Queryable.fromSync(x.items).avarage(x => x.realOverall)) + } + }); + + query.toList().then(data => { + console.table(data) + }); +} + +main(); + + diff --git a/src/implements/methods.ts b/src/implements/methods.ts index fe3bcd1..43d764e 100644 --- a/src/implements/methods.ts +++ b/src/implements/methods.ts @@ -4,9 +4,11 @@ import { WhereClause, SelectClause, SelectManyClause, JoinClause, LeftJoinClause, OrderByClause, OrderByDescendingClause, GroupByClause, GroupJoinClause, FirstClause, LastClause, CountClause, SumClause, AvarageClause, MinClause, MaxClause, SingleClause, TakeClause, SkipWhileClause, - SkipClause, TakeWhileClause, AnyClause, ContainsClause + SkipClause, TakeWhileClause, AnyClause, ContainsClause, AllClause } from "../methods"; import { Queryable } from './queryable'; +import { DistinctClause } from '../methods/distinct'; +import { ConcatClause } from '../methods/concat'; export class IteratorMethods implements IMethods { @@ -25,7 +27,7 @@ export class IteratorMethods implements IMethods { clone(): IMethods { const _cloneCollection = Object.assign([], this._iteratorCollection); - return new IteratorMethods(_cloneCollection,this._source); + return new IteratorMethods(_cloneCollection, this._source); } where(iterator: (entity: T) => boolean): IMethods { @@ -75,6 +77,16 @@ export class IteratorMethods implements IMethods { return this as any; } + distinct(comparer?: (aEntity: T, bEntity: T) => boolean): IMethods { + this._iteratorCollection.push(new DistinctClause(comparer)); + return this; + } + + concat(another: T[] | Promise): IMethods { + this._iteratorCollection.push(new ConcatClause(another)); + return this; + } + // Return S[] | Promise toList(): any { return this.runIterators(); @@ -175,6 +187,12 @@ export class IteratorMethods implements IMethods { }); } + all(iterator: (entity: T) => boolean): Promise { + return this.filterReturn(this.toList(), (data) => { + return new AllClause(iterator).execute(data); + }); + } + contains(entity: T): Promise { return this.filterReturn(this.toList(), (data) => { return new ContainsClause(entity).execute(data); diff --git a/src/intefaces/methods.interface.ts b/src/intefaces/methods.interface.ts index 7d4a432..1c49713 100644 --- a/src/intefaces/methods.interface.ts +++ b/src/intefaces/methods.interface.ts @@ -16,6 +16,8 @@ export interface IMethods { leftJoin(source: S[] | Promise, iterator: (aEntity: T, bEntity: S) => boolean): IMethods; groupBy(iterator: (entity: T) => any): IMethods<{ key: any, items: T[] }>; groupJoin(source: S[] | Promise, joinIterator: (aEntity: T, bEntity: S) => boolean, groupIterator: (entity: { x: T, y: S }) => any): IMethods<{ key: any, items: T[] }>; + distinct(comparer?: (aEntity: T, bEntity: T) => boolean): IMethods + concat(another: T[] | Promise): IMethods // Execute methods toList(): Promise; @@ -25,6 +27,7 @@ export interface IMethods { max(iterator: (entity: T) => S): Promise; avarage(iterator?: (entity: T) => S): Promise; any(iterator: (entity: T) => boolean): Promise; + all(iterator: (entity: T) => boolean): Promise; contains(entity: T): Promise; // Methods with optional iterator @@ -34,5 +37,5 @@ export interface IMethods { lastOrDefault(iterator?: (entity: T) => boolean): Promise; single(iterator?: (entity: T) => boolean): Promise; singleOrDefault(iterator?: (entity: T) => boolean): Promise; - count(iterator?: (entity: T) => boolean): Promise; + count(iterator?: (entity: T) => boolean): Promise; } \ No newline at end of file diff --git a/src/methods/all.ts b/src/methods/all.ts new file mode 100644 index 0000000..b4a6a63 --- /dev/null +++ b/src/methods/all.ts @@ -0,0 +1,21 @@ +import { IIterator } from "../intefaces/iterator.interface"; +import { BaseIterator } from "../implements/baseIterator"; + +export class AllClause extends BaseIterator implements IIterator { + + _iterator: (item: T) => boolean; + + execute(source: T[] | any[]): T[] | T | any { + if (!source) return false; + else { + return (source as T[]).every((x) => { + return this._iterator(x); + }); + } + } + + constructor(func: (item: T) => boolean) { + super(); + this._iterator = func; + } +} \ No newline at end of file diff --git a/src/methods/any.ts b/src/methods/any.ts index f8700ff..037e8af 100644 --- a/src/methods/any.ts +++ b/src/methods/any.ts @@ -8,7 +8,7 @@ export class AnyClause extends BaseIterator implements IIterator { execute(source: T[] | any[]): T[] | T | any { if (!source) return false; else { - return (source as T[]).every((x) => { + return (source as T[]).some((x) => { return this._iterator(x); }); } diff --git a/src/methods/concat.ts b/src/methods/concat.ts new file mode 100644 index 0000000..25bd111 --- /dev/null +++ b/src/methods/concat.ts @@ -0,0 +1,15 @@ +import { BaseIterator } from '../implements'; +import { IIterator } from '../intefaces'; + +export class ConcatClause extends BaseIterator implements IIterator{ + nextSource; + + execute(source: T[]): T[] { + return source.concat(this.nextSource); + } + + constructor(another: T[] | Promise) { + super(); + this.nextSource = another; + } +} \ No newline at end of file diff --git a/src/methods/distinct.ts b/src/methods/distinct.ts new file mode 100644 index 0000000..27f66c6 --- /dev/null +++ b/src/methods/distinct.ts @@ -0,0 +1,17 @@ +import { IIterator } from '../intefaces'; +import { BaseIterator } from '../implements'; +import { Utils } from '../utils'; + +export class DistinctClause extends BaseIterator implements IIterator { + _comparer: (aEntity: T, bEntity: T) => boolean; + + execute(source: T[]): T[] { + if (!this._comparer) + return Utils.distinct(source); + } + + constructor(comparer?: (aEntity: T, bEntity: T) => boolean) { + super(); + this._comparer = comparer; + } +} \ No newline at end of file diff --git a/src/methods/index.ts b/src/methods/index.ts index be1e08e..95a6865 100644 --- a/src/methods/index.ts +++ b/src/methods/index.ts @@ -20,4 +20,5 @@ export * from './skipWhile'; export * from './sum'; export * from './take'; export * from './takeWhile'; -export * from './where'; \ No newline at end of file +export * from './where'; +export * from './all'; \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..78e028b --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1 @@ +export * from './object'; \ No newline at end of file diff --git a/src/utils/object.ts b/src/utils/object.ts index 5c04ded..358353d 100644 --- a/src/utils/object.ts +++ b/src/utils/object.ts @@ -2,4 +2,10 @@ export class Utils { public static isPromise(obj) { return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; } + + public static distinct(array: any[]) { + return array.filter((val, index, self) => { + return self.indexOf(val) === index; + }).filter(x => x != undefined); + } } \ No newline at end of file diff --git a/test/distinct.ts b/test/distinct.ts new file mode 100644 index 0000000..846b988 --- /dev/null +++ b/test/distinct.ts @@ -0,0 +1,14 @@ +import { players, continents, nations } from './source'; +import { expect } from 'chai'; +import { Queryable } from '../dist'; + +describe('distinct', () => { + const query = Queryable.from(nations) + .join(continents, (x, y) => x.areaId === y.id) + .select(x => x.y.areaName); + + it('can run', async () => { + const first = await query.distinct(); + expect(first).to.not.equal(undefined); + }) +}); \ No newline at end of file diff --git a/test/first.ts b/test/first.ts new file mode 100644 index 0000000..12bbb84 --- /dev/null +++ b/test/first.ts @@ -0,0 +1,13 @@ +import { players, continents, nations } from './source'; +import { expect } from 'chai'; +import { Queryable } from '../dist'; + +describe('first', () => { + const query = Queryable.from(players); + + it('can run', async () => { + const first = await query.first(); + expect(first).to.not.equal(undefined); + expect(first).to.not.equal(null); + }) +}); \ No newline at end of file