Skip to content

Commit

Permalink
Update new library design
Browse files Browse the repository at this point in the history
- Add fromsync methods
- Update document
- Update example
- Update index.ts to export all api
  • Loading branch information
jinhduong committed Apr 25, 2018
1 parent 715a8b7 commit c555868
Show file tree
Hide file tree
Showing 34 changed files with 231 additions and 150 deletions.
127 changes: 87 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,113 @@
.NET LINQ for Javascript, written by TypeScript.
- Provide `IQueryable<T>`, it's reusable, also variable and use `iterator list` for holding query and execute.
- Contains almost the original .NET and some extends methods.
- Support `Promise` like a input source.

### Example
### Basic example
```ts
const queryable = new Queryable<{ name, nationId, overall, skills }>();
let query = Queryable
.from(nations)
.join(continents, (x, y) => x.areaId === y.id)
.groupBy(o => o.y.areaName)
.select(x => {
return {
area: x.key,
total: Queryable.fromSync(x.items).count() // Here will return number, not Promise<number>
}
})
const asyncData = query.count() // Will return Promise<{area:string, total:number}>
asyncData.then(data => {
console.log(data);
// [
// {area: 'Euro': total: 2},
// {area:'South Ameria', total: 1}
// ]
});
```

let promiseApi = new Promise((resolve, reject) => {
### Sample data ⚽
This document will use below data to make examples :
```ts
let players = new Promise((resolve, reject) => {
// skills: attack, stamia, speed, shoot
console.log('get players...');
setTimeout(() => {
console.log('...get data');
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, 80, 89, 81] },
{ name: 'Salah', overall: 89, nationId: 4, skills: [88, 82, 97, 86] }
]);
}, 1000);
})

let nations = [
{ id: 1, name: 'Portugal', areaId: 1 },
{ id: 2, name: 'Argentina', areaId: 2 },
{ id: 3, name: 'France', areaId: 1 },
{ id: 4, name: 'Egypt', areaId: 3 }
]
let nations: Promise<{ id, name, areaId }[]> = new Promise(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);
})
```

### Queryable
- `.from` => `Promise<any>`:
All data source will be converted to `Promise` notwithstanding that source is Promise or noramlly array data. Then once we call `toList()`, `count()`, `first()`... or any `execute methods` it will be return a `Promise`.

let continents = [
{ id: 1, areaName: 'Euro' },
{ id: 2, areaName: 'South America' },
]
```ts
let query = Queryable
.from(nations)
.join(continents, (x, y) => x.areaId === y.id)
.groupBy(o => o.y.areaName)

function main() {
// Just query
let query = queryable
.from(promiseApi)
.join(nations, (pl, na) => pl.nationId === na.id)
.leftJoin(continents, (plNation, co) => plNation.y.areaId === co.id)
.select(o => {
return {
playerName: o.x.name,
overall: o.x.overall,
nation: o.y.name,
area: o.areaName
}
})
.groupBy(x => x.area)
.orderByDescending(x => x.key);
const asyncData = query.count() // Will return Promise<number>
asyncData.then(num => {
console.log(num);
// 2
})
```

// call toList() to execute data
const data = query.toList().then(data => {
console.log(data);
});

// [{ key: "Euro", items: [{ playerName: "Ronaldo",... }, { playerName:"Mbappe",... }] },
// { key: "South America", items: [ { playerName: "Messi" }]
- `.fromSync` => `any`:
It's often use inside sub-query whese are after all data already retrieve successfully or you make sure the inpurt source is sync data like as `Array`.
``` ts
let query = Queryable
.from(nations)
.join(continents, (x, y) => x.areaId === y.id)
.groupBy(o => o.y.areaName)
.select(x => {
return {
area: x.key,
total: Queryable.fromSync(x.items).count() // Here will return number, not Promise<number>
}
})
const asyncData = query.count() // Will return Promise<{area:string, total:number}>
asyncData.then(data => {
console.log(data);
// [
// {area: 'Euro': total: 2},
// {area:'South Ameria', total: 1}
// ]
}

main();
});
```

> **More**: at `from` method, all input sources at `from`, `join`, `leftJoin` methods,... will be converted to `Promise` and use `Promise.all` to execute and then use `Iterator collection` to query data and once faced `excuting` methods like as `toList()`, `first()` it will return data which be inside a Promise.

### Process
- [x] from
- [x] where
Expand Down
8 changes: 3 additions & 5 deletions src/demo/test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Queryable } from "../implements/queryable";

let promiseApi = new Promise((resolve, reject) => {
let players = new Promise((resolve, reject) => {
// skills: attack, stamia, speed, shoot
console.log('get players...');
setTimeout(() => {
Expand Down Expand Up @@ -40,16 +40,14 @@ let continents = new Promise<{ id, areaName }[]>(resolve => {
function main() {
// Just query not execute query
console.time('querytime');
let query =
Queryable
let query = Queryable
.from(nations)
.join(continents, (x, y) => x.areaId === y.id)
.groupBy(o => o.y.areaName)
.select(x => {
let _tmp = {};
return {
area: x.key,
total: Queryable.from(x.items).count()
total: Queryable.fromSync(x.items).count()
}
})

Expand Down
File renamed without changes.
4 changes: 4 additions & 0 deletions src/implements/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './baseIterator';
export * from './methods';
export * from './syncMethods';
export * from './queryable';
105 changes: 50 additions & 55 deletions src/implements/methods.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,14 @@
import { Methods } from "../intefaces/methods.interface";
import { IIterator } from "../intefaces/iterator.interface";
import { WhereClause } from "../methods/where";
import { SelectClause } from "../methods/select";
import { Utils } from "../utils/object";
import { FirstClause } from "../methods/first";
import { LastClause } from "../methods/last";
import { CountClause } from "../methods/count";
import { TakeClause } from "../methods/take";
import { SkipClause } from "../methods/skip";
import { SkipWhileClause } from "../methods/skipWhile";
import { TakeWhileClause } from "../methods/takeWhile";
import { JoinClause } from "../methods/join";
import { LeftJoinClause } from "../methods/leftJoin";
import { SelectManyClause } from "../methods/selectMany";
import { SumClause } from "../methods/sum";
import { MinClause } from "../methods/min";
import { MaxClause } from "../methods/max";
import { SingleClause } from "../methods/single";
import { AnyClause } from "../methods/any";
import { AvarageClause } from "../methods/average";
import { OrderByClause } from "../methods/orderBy";
import { OrderByDescendingClause } from "../methods/orderByDescending";
import { GroupByClause } from "../methods/groupBy";
import { ContainsClause } from "../methods/contains";
import { GroupJoinClause } from "../methods/groupJoin";

export class IteratorMethods<T> implements Methods<T> {
import { IMethods, IIterator } from "../intefaces";
import { Utils } from '../utils/object';
import {
WhereClause, SelectClause, SelectManyClause, JoinClause, LeftJoinClause, OrderByClause,
OrderByDescendingClause, GroupByClause, GroupJoinClause, FirstClause, LastClause, CountClause,
SumClause, AvarageClause, MinClause, MaxClause, SingleClause, TakeClause, SkipWhileClause,
SkipClause, TakeWhileClause, AnyClause, ContainsClause
} from "../methods";

export class IteratorMethods<T> implements IMethods<T> {

groupJoin<S>(source: S[],
joinIterator: (aEntity: T, bEntity: S) => boolean,
groupIterator: (entity: { x: T; y: S; }) => any): Methods<{ key: any; items: T[]; }> {
this._iteratorCollection.push(new GroupJoinClause(source, joinIterator, groupIterator));
return this as any;
}
// Contains all iterators
_iteratorCollection: Array<IIterator<T>> = [];

Expand All @@ -46,48 +22,56 @@ export class IteratorMethods<T> implements Methods<T> {
this._source = source;
}

where(iterator: (entity: T) => boolean): Methods<T> {
where(iterator: (entity: T) => boolean): IMethods<T> {
this._iteratorCollection.push(new WhereClause(iterator));
return this;
}

select<S>(iterator: (entity: T) => S): Methods<S> {
select<S>(iterator: (entity: T) => S): IMethods<S> {
this._iteratorCollection.push(new SelectClause(iterator))
return this as any;
}

selectMany<S>(iterator: (entity: T) => S): Methods<S> {
selectMany<S>(iterator: (entity: T) => S): IMethods<S> {
this._iteratorCollection.push(new SelectManyClause(iterator))
return this as any;
}

join<S, U>(source: S[] | Promise<S[]>, iterator: (aEntity: T, bEntity: S) => boolean): Methods<U> {
join<S, U>(source: S[] | Promise<S[]>, iterator: (aEntity: T, bEntity: S) => boolean): IMethods<U> {
this._iteratorCollection.push(new JoinClause(source, iterator));
return this as any;
}

leftJoin<S, U>(source: S[] | Promise<S[]>, iterator: (aEntity: T, bEntity: S) => boolean): Methods<U> {
leftJoin<S, U>(source: S[] | Promise<S[]>, iterator: (aEntity: T, bEntity: S) => boolean): IMethods<U> {
this._iteratorCollection.push(new LeftJoinClause(source, iterator));
return this as any;
}

orderBy(iterator: (entity: T) => T): Methods<T> {
orderBy(iterator: (entity: T) => T): IMethods<T> {
this._iteratorCollection.push(new OrderByClause(iterator));
return this;
}

orderByDescending(iterator: (entity: T) => T): Methods<T> {
orderByDescending(iterator: (entity: T) => T): IMethods<T> {
this._iteratorCollection.push(new OrderByDescendingClause(iterator));
return this;
}

groupBy(iterator: (entity: T) => any): Methods<{ key: any; items: T[]; }> {
groupBy(iterator: (entity: T) => any): IMethods<{ key: any; items: T[]; }> {
this._iteratorCollection.push(new GroupByClause(iterator));
return this as any;
}

toList<S>(): Promise<S[]> {
return this.runIterators() as any;
groupJoin<S>(source: S[],
joinIterator: (aEntity: T, bEntity: S) => boolean,
groupIterator: (entity: { x: T; y: S; }) => any): IMethods<{ key: any; items: T[]; }> {
this._iteratorCollection.push(new GroupJoinClause(source, joinIterator, groupIterator));
return this as any;
}

// Return S[] | Promise<S[]>
toList<S>(): any {
return this.runIterators();
}

first(iterator?: (entity: T) => boolean): Promise<T> {
Expand Down Expand Up @@ -115,11 +99,12 @@ export class IteratorMethods<T> implements Methods<T> {
}

count(iterator?: (entity: T) => boolean): Promise<number> {
return this.toList().then((data: T[]) => {
return this.filterReturn(this.toList(), (data) => {
return (new CountClause(iterator).execute(data) || null) as number;
});
}) as any;
}


sum<S>(iterator: (entity: T) => S): Promise<number> {
return this.toList().then((data: T[]) => {
return (new SumClause(iterator).execute(data) || null) as number;
Expand Down Expand Up @@ -158,22 +143,22 @@ export class IteratorMethods<T> implements Methods<T> {
});
}

take(value: number): Methods<T> {
take(value: number): IMethods<T> {
this._iteratorCollection.push(new TakeClause(value));
return this;
}

skip(value: number): Methods<T> {
skip(value: number): IMethods<T> {
this._iteratorCollection.push(new SkipClause(value));
return this;
}

skipWhile(iterator: (entity: T) => boolean): Methods<T> {
skipWhile(iterator: (entity: T) => boolean): IMethods<T> {
this._iteratorCollection.push(new SkipWhileClause(iterator));
return this;
}

takeWhile(iterator: (entity: T) => boolean): Methods<T> {
takeWhile(iterator: (entity: T) => boolean): IMethods<T> {
this._iteratorCollection.push(new TakeWhileClause(iterator));
return this;
}
Expand All @@ -192,7 +177,19 @@ export class IteratorMethods<T> implements Methods<T> {

// Private functions

private runIterators(): Promise<T[]> {
// This function detect the input parameter is Promise or plain array data
// if is promise => call promise and return from callback
// opposite => return from callback
private filterReturn(obj: T[] | Promise<T[]>, promiseCallback: Function) {
if (Utils.isPromise(obj)) {
return (obj as Promise<T[]>).then((data: T[]) => {
return promiseCallback(data);
})
} else
return promiseCallback(obj);
}

runIterators(): Promise<T[]> | T[] {

let _result: T[] = [];
let _nextSources = {};
Expand All @@ -217,10 +214,9 @@ export class IteratorMethods<T> implements Methods<T> {

return new Promise(resolve => {
Promise.all(_promises).then((responseDatas: any[]) => {
let _index = 0;

// Set from method's source
_result = responseDatas[0];
let _index = 0;
_result = responseDatas[0]; // Set from method's source

for (let i = 0, li = this._iteratorCollection.length; i < li; i++) {

Expand All @@ -237,7 +233,6 @@ export class IteratorMethods<T> implements Methods<T> {
});

resolve(_result);

});
});
}
Expand Down

0 comments on commit c555868

Please sign in to comment.