Skip to content

Commit

Permalink
Merge pull request #2 from jinhduong/linq
Browse files Browse the repository at this point in the history
Merge from linq to master
  • Loading branch information
jinhduong committed Apr 25, 2018
2 parents 1767fb4 + 3f41375 commit f43707b
Show file tree
Hide file tree
Showing 43 changed files with 526 additions and 241 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}\\dist\\out-tsc\\demo\\test.js",
"program": "${workspaceFolder}\\dist\\demo\\test.js",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
Expand Down
130 changes: 88 additions & 42 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 which are after all data already retrieved successfully or you make sure the input 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.toList() // 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 Expand Up @@ -93,13 +140,12 @@ main();
- [ ] elementAtOrDefault
- [ ] orderByDescending
- [ ] defaultIfEmpty
- [ ] contains
- [x] contains
- [ ] reserve
- [ ] sequenceEqual
- [x] any : `Promise<boolean>`
- [ ] all
- [x] count : `Promise<number>`
- [ ] longCount
- [x] min : `Promise<number>`
- [x] max : `Promise<number>`
- [x] sum : `Promise<number>`
Expand Down
76 changes: 42 additions & 34 deletions demo/index.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,57 @@
import { Queryable } from '../dist/out-tsc/index.js';
import { Queryable } from '../dist/index.js';

const queryable = new Queryable();
// let promiseApi = new Promise((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, 80, 89, 81] },
// { name: 'Salah', overall: 89, nationId: 4, skills: [88, 82, 97, 86] }
// ]);
// }, 1000);
// })

let promiseApi = new Promise((resolve, reject) => {
// skills: attack, stamia, speed, shoot
let nations = new Promise(resolve => {
console.log('get nations...');
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: 'Salah', overall: 89, nationId: 4, skills: [88, 82, 97, 86] }
{ id: 1, name: 'Portugal', areaId: 1 },
{ id: 2, name: 'Argentina', areaId: 2 },
{ id: 3, name: 'France', areaId: 1 },
{ id: 4, name: 'Egypt', areaId: 3 }
]);
}, 1000);
}, 2000);
})

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 continents = new Promise(resolve => {
console.log('get continents...');
setTimeout(() => {
resolve([
{ id: 1, areaName: 'Euro' },
{ id: 2, areaName: 'South America' },
]);
}, 2300);
})

let continents = [
{ id: 1, areaName: 'Euro' },
{ id: 2, areaName: 'South America' },
]

function main() {
// Just query not execute query
console.time('querytime');
let query = queryable
.from(promiseApi)
.join(nations, (x, y) => x.nationId === y.id)
.leftJoin(continents, (o, z) => o.y.areaId === z.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);
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()
}
})


console.timeEnd('querytime');

Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@
"typedoc": "^0.11.1",
"typescript": "^2.8.1",
"webpack": "^4.6.0"
},
"dependencies": {
"tslib": "^1.9.0"
}
}
63 changes: 35 additions & 28 deletions src/demo/test.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,56 @@
import { Queryable } from "../implements/queryable";

const queryable = new Queryable<{ name, nationId, overall, skills }>();

let promiseApi = new Promise((resolve, reject) => {
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);
})

let continents = [
{ id: 1, areaName: 'Euro' },
{ id: 2, areaName: 'South America' },
]

function main() {
// Just query not execute query
console.time('querytime');
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);
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()
}
})


console.timeEnd('querytime');

Expand Down
17 changes: 17 additions & 0 deletions src/implements/baseIterator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IIterator } from "../intefaces/iterator.interface";

export class BaseIterator<T> implements IIterator<T> {
nextSource: any[] | Promise<any>;

replaceBySyncSource?(syncSource: T[]) {
this.nextSource = syncSource;
}

hasSource(): boolean {
return this.nextSource !== null && this.nextSource !== undefined;
}

execute(source: T[]) {
throw new Error("Method not implemented.");
}
}
Loading

0 comments on commit f43707b

Please sign in to comment.