gf-js
Version:
Attach most of function from lodash to Javascript GeneratorFunction and Array.
682 lines (587 loc) • 21.3 kB
Markdown
# Install with
```
npm install gf-js --save
```
# gf-js
Write some extensions function for [**GeneratorFunction**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction) and [**Array**](https://developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Global_Objects/Array). We expand and attach most of [`lodash`](https://lodash.com/) functions to `Array` and `GeneratorFunction` by applying technique [`Extension Methods` or `Extension Function`](https://www.loekvandenouweland.com/content/extension-methods-in-javascript.html) in `Javascript`.
Eg:
The normal using
```js
const _ = require('lodash');
const GF = require('gf-js');
let x = [1,2,3,4,5]
// lodash function
_.chunk(x,2); //[ [1, 2], [3, 4], [5]]
// Using chain function
_(x).chunk(2)
.toValues();
// Our module can attach function to `Array` class
x.chunk(2); // [[1, 2], [3, 4], [5]]
// Our module can chunk by an
// array of size (not only one fixed size)
x.chunk([2,3]); // [[1,2], [3,4,5]]
```
We can chunk the infinite-loop `Generator`
```js
//infinite-loop `Generator`
function *number(){
for(let i = 0; ;i++) yield i;
}
//chunker
let chunker = number().ichunk(2);
for(let v in chunker){
console.log(v);
// [0, 1]
// [2, 3]
// [4, 5]
// ...
// ...
// loop infinite - please press `Ctrl-B` to break
}
```
## What is `GeneratorFunction(GF)`?
`GeneratorFunction` is `Function` can return a [`Generator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator "The Generator object is returned by a generator function and it conforms to both the iterable protocol and the iterator protocol.") object. . Instead of saving all entrie data as an `Array`, the `Generator` generates the next value as needed base on the previous value. So that the `Generator` does not waste memory, and it can be generator infinite values. You can read more information in [function*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)
Eg:
The normal `GeneratorFunction` generates 2 values
```js
//The normal `GeneratorFunction` generates 2 values
function* generator(i) {
yield i;
yield i + 10;
}
var gen = generator(10);
console.log(gen.next().value);
// expected output: 10
console.log(gen.next().value);
// expected output: 20
```
The infinite-loop `GeneratorFunction` generates all positive numbers
```js
//The infinite-loop `GeneratorFunction` generates all positive numbers
function *number(){
for(let i = 0; ;i++) yield i;
}
//This code prints unfinite number in the screen.
//You must be break it by `Ctrl+B`
for(let item of number()){
console.log(item);
}
```
## How to use?
Open [sample](./sample/) folder to see a lot of tutorials
## API
### Initialize
Before using, you have to import it in your project first
```js
const GF = require('gf-js')
```
After that all functions is pluged-in to `Array` and `Generator` and you can use it any where.
All APIs are add-on to JavaScript [`Array`](https://developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Global_Objects/Array) and [`GeneratorFunction`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction) extension functions.
> Note: Because the `Javascript` language architecture is not good as `C#` or `Python`, the `Array` class does not inherit from `Generator`, we must deploy the functions for both and write the functions to convert between them. Almost functions start with `i` return a `Generator`, and without `i` return an `Array`
### `GF.prototype.toArray()`
Convert `GeneratorFunction` (GF) instance to `Array` instance
Eg:
```js
function* generator(i) {
yield i;
yield i + 10;
}
let g = generator(5);
let arr = g.toArray(); //arr = [0,1,2,3,4,5]
```
### `Arrray.prototype.toGenerator()`
Convert `Array` instance to `Generator`
```js
let g = [1,2,3,4,5].toGenerator()
```
now `g` equal to
```js
function* generator() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
}
g = generator()
```
### `Array.prototype.sum()`
### `GF.prototype.sum()`
Sum all elements in this Array or GF
```js
console.log([1,2,3].sum()) //1+2+3 = 6
```
### `Array.prototype.count()`
### `GF.prototype.count()`
Count the number of element
```js
console.log([1,2,3].count()) //3
```
### `Array.prototype.avg()`
### `GF.prototype.avg()`
Average of all elements in this array or GF
```js
console.log([1,2,3].avg()) // (1+2+3)/ 3 = 2
```
### `GF.prototype.forEach(callback)`
Implement [`Array.prototype.forEach(callback)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) for GF
### `GF.prototype.imapBy(callback)`
### `GF.prototype.imapBy(callback)`
### `Array.prototype.mapBy(callback)`
### `Array.prototype.imapBy(callback)`
Create a new array with the results of calling a provided function on every element in the calling array. Same as [`Array.prototype.map(callback)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
The `callback` parameter is `GetCallBack`
```js
/**
* @typedef {String|Function|Array<string>} GetCallback
*/
```
It can be a function
```js
function *generator(n){
for (let i =0 ; i < n; i++) yield i;
}
let g = generator(10);
let h = g.imapBy(i=>i*2).toArray(); //[0,2,4,6,8,10,12,14,16,18]
let k = g.mapBy(i=>i*3); //[0,3,6,9,12,15,18,21,24,27]
```
Or `string` if the values of `Array` or `Generator` are key-value pair `object`
```js
const users = [
{ user: 'barney', age: 36, active: true, heigh: 10 },
{ user: 'barney', age: 36, active: true, heigh: 12 },
{ user: 'fred', age: 40, active: false, heigh: 11 },
{ user: 'barney', age: 40, active: true, heigh: 12 }
];
const usernames = users.mapBy('user');
// ['ronaldo','barney','fred','barney']
```
Or `Array<string>`
```js
const shortUsers = users.mapBy(['user','active'])
// [
// { user: 'barney', active: true },
// { user: 'barney', active: true },
// { user: 'fred', active: false },
// { user: 'barney', active: true }
// ]
```
See [map.spl.js](./sample/map.spl.js) for more samples.
### `Array.zip(...iterables)`
### `GF.zip(...iterables)`
The same python build-in [`zip` function ](https://docs.python.org/3/library/functions.html#zip)
```js
let x = [1,2,3,4];
let y = ['a','b','c','d'];
let z = Array.zip(x,y);
//z = [[1,'a'],[2,'b'],[3,'c'],[4,'d']]
```
### `Array.izip(...iterables)`
### `GF.izip(...iterables)`
The same python build-in [`zip` function ](https://docs.python.org/3/library/functions.html#zip) but returns `Generator` instead of `Array`
```js
let x = [1,2,3,4];
let y = ['a','b','c','d'];
let z = Array.izip(x,y);
for(let [a,b] of z){
console.log(a,b)
}
```
the display is
```
1 a
2 b
3 c
4 d
```
### `Array.ichain(...iterables)`
### `GF.ichain(...iterables)`
The same python [`itertools.chain`](https://docs.python.org/2/library/itertools.html#itertools.chain) function.
```js
let x = [1,2,3,4];
let y = ['a','b','c','d'];
let z = Array.ichain(x,y);
for(let e of z){
console.log(e)
}
```
the display is
```
1
2
3
4
a
b
c
d
```
### `Array.chain(...iterables)`
### `GF.chain(...iterables)`
The same python [`itertools.chain`](https://docs.python.org/2/library/itertools.html#itertools.chain) function but returns Array instead of GF.
```js
let x = [1,2,3,4];
let y = ['a','b','c','d'];
let z = Array.chain(x,y);
//z = [1,2,3,4,'a','b','c','d']
```
See [chunk-chain.spl.js](./sample/chunk-chain.spl.js) to see more samples.
### `Array.prototype.chunk(size)`
### `GF.prototype.chunk(size)`
### `Array.prototype.ichunk(size)`
### `GF.prototype.ichunk(size)`
Creates an array of elements split into groups the length of size. If array can't be split evenly, the final chunk will be the remaining elements. Implememt lodash [`chunk`](https://lodash.com/docs/#chunk). But the `size` parameter is more flexible
The `size` parameter can be a number
```js
let x = [1,2,3,4,5,6,7];
let y = x.chunk(2);
// y = [[1,2],[3,4],[5,6],[7]]
function* generator(n) {
for(let i = 0; i < n; i++) yield i;
}
let g = generator(7);
let h = g.chunk(2);
// h = [[0,1],[2,3],[4,5],[6]]
```
Or can be an array of numbers
```js
let y = x.chunk([2,3,4])
// y = [
// [1, 2],
// [3,4,5],
// [6,7] // not enough 4 elements because x is too short
// ]
```
Or can be a `Generator`
```js
function *size(){
yield 2;
yield 3;
yield 4;
}
let y = x.chunk(size())
// y = [
// [1, 2],
// [3,4,5],
// [6,7] // not enough 4 elements because x is too short
// ]
```
An infinite-loop `Generator`
```js
let x = [1,2,3,4,5,6,7,8,9,10, 11]
let y = x.chunk([2,3].repepat()) //loop 2, 3 infinite
// y = [
// [1, 2], //2
// [3,4,5], //3
// [6,7], //2
// [8, 9, 10], //3
// [11] // remain only one item
// ]
```
See [chunk-chain.spl.js](./sample/chunk-chain.spl.js) to see more samples.
### `Array.prototype.ifilterBy(callback)`
### `GF.prototype.ifilterBy(callback)`
### `GF.prototype.filterBy(callback)`
### `Array.prototype.filterBy(callback)`
Same [`Array.prototype.filter(callback)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter). A `callback` not only a function, but also a `DecisionCallback`.
```js
/**
* A string, a function or a object<key,value> object.
* @typedef {String|Function|Object.<string,string>} DecisionCallback
*/
```
`callback` is function
```js
function* generator(n) {
for(let i = 0; i < n; i++) yield i;
}
let g = generator(7);
let h = g.filterBy(x=>x%3==0);
// h = [0,3,6]
```
```js
const users = [
{ user: 'barney', age: 36, active: true, heigh: 10 },
{ user: 'barney', age: 36, active: true, heigh: 12 },
{ user: 'fred', age: 40, active: false, heigh: 11 },
{ user: 'barney', age: 40, active: true, heigh: 12 }
]
users.filterBy(({active})=>active);
// [
// { user: 'barney', age: 36, active: true, heigh: 10 },
// { user: 'barney', age: 36, active: true, heigh: 12 },
// ]
```
`callback` is string
```js
users.filterBy(`active`);
// [
// { user: 'barney', age: 36, active: true, heigh: 10 },
// { user: 'barney', age: 36, active: true, heigh: 12 },
// ]
```
`callback` is object
```js
users.filterBy({heigh:12});
// [
// { user: 'barney', age: 36, active: true, heigh: 12 },
// { user: 'barney', age: 40, active: true, heigh: 12 }
// ]
users.filterBy({heigh:12, age:36});
// [
// { user: 'barney', age: 36, active: true, heigh: 12 },
// ]
```
See [filter.spl.js](./sample/filter.spl.js) for more samples.
### `Array.range(...args)`
### `Array.irange(...args)`
Same python build-in [range](https://docs.python.org/2/library/functions.html#range) function.
We can user function in two types:
`range`(_stop_)
`range`(_start_, _stop_[, _step_])
```js
let x = Array.range(5); //x = [0,1,2,3,4]
let y = Array.range(1,11); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let z = Array.range(0, -10, -1); //[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
```
### `GF.prototype.groupBy(callback=undefined)`
### `Array.prototype.groupBy(callback=undefined)`
Group all items with the same key in the same group.
The `callback` parameter is `GetCallback`. See `mapBy` method.
```js
let x = range(10); //x=[0,1,2,3,4,5,6,7,8,9]
let g = x.groupBy(x=>x%2);
//g = {
// '0':[0,2,4,6,8],
// '1':[1,3,5,7,9]
// }
const users = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 36, 'active': false },
{ 'user': 'mary', 'age': 40, 'active': true },
{ 'user': 'lazzy', 'age': 40, 'active': false },
];
users.groupBy('age');
// {
// '36': [
// { user: 'barney', age: 36, active: true },
// { user: 'fred', age: 36, active: false }
// ],
// '40': [
// { user: 'mary', age: 40, active: true },
// { user: 'lazzy', age: 40, active: false }
// ]
// }
```
See [group-partition-count-distinct.spl.js](./sample/group-partition-count-distinct.spl.js) for more samples.
### `GF.prototype.countBy(callback=undefined)`
### `Array.prototype.countBy(callback=undefined)`
The same as `groupBy` function but instead of return array of items, just return the number of item in the same group.
```js
const users = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 36, 'active': false },
{ 'user': 'mary', 'age': 40, 'active': true },
{ 'user': 'lazzy', 'age': 40, 'active': false },
];
users.countBy('age');
//{ '36': 2, '40': 2 }
```
See [group-partition-count-distinct.spl.js](./sample/group-partition-count-distinct.spl.js) for more samples.
### `GF.prototype.partition(callback=undefined)`
### `Array.prototype.partition(callback=undefined)`
The same as `groupBy` but not return the keys, only array of values.
See [group-partition-count-distinct.spl](./sample/group-partition-count-distinct.spl) for more samples.
### `GF.prototype.distinctBy(callback=undefined)`
### `GF.prototype.idistinctBy(callback=undefined)`
### `Array.prototype.distinctBy(callback=undefined)`
### `Array.prototype.idistinctBy(callback=undefined)`
Filter the difference items in this Array. The `callback` parameter is `GetCallback`. See `mapBy` method.
```js
let x = [1,2,3,4,5,6,5,7,6,8];
let d1 = x.distinct(); //d1 = [1,2,3,4,5,6,7,8]
let d2 = x.distinct(x=>x%5); //d2 = [1,2,3,4,5];
```
See [group-partition-count-distinct.spl](./sample/group-partition-count-distinct.spl) for more samples.
### `GF.prototype.minBy(callback, includeIndex = false)`
### `Array.prototype.minBy(callback, includeIndex = false)`
### `GF.prototype.maxBy(callback, includeIndex = false)`
### `Array.prototype.maxBy(callback, includeIndex = false)`
Get the min, max by `callback`. The `callback` parameter is `GetCallback`. Instead of `Math.min` and `Math.max` functions, the return value is the original value, not the value after apply `callback`. If `includeIndex` is `true` return pair of value and index of value
```js
x = [1, 2, 3, 4, 5, 4]
console.log(x.min(), x.max())
y = [
[1, 3, -4],
[0],
[-0],
[1, 2],
[1, 3],
[1, 3, -5],
[0, 5]
]
console.log(y.min(includeIndex = true))
console.log(y.max(includeIndex = false))
console.log(y.minBy(v => v[1]))
console.log(y.minBy(v => v[1]))
var users = [
{ 'user': 'barney', 'age': 36, 'active': true, 'heigh': 10 },
{ 'user': 'barney', 'age': 36, 'active': true, 'heigh': 12 },
{ 'user': 'fred', 'age': 40, 'active': false, 'heigh': 11 },
{ 'user': 'barney', 'age': 40, 'active': true, 'heigh': 12 },
];
```
See [min-max.spl.js](./sample/min-max.spl.js) for more samples.
### `GF.prototype.min(includeIndex = false)`
### `Array.prototype.min(includeIndex = false)`
### `GF.prototype.max(includeIndex = false)`
### `Array.prototype.max(includeIndex = false)`
Special functions of `minBy` and `maxBy`. Not apply `callback` for item.
### `GF.prototype.argMinBy(callback)`
### `Array.prototype.argminBy(callback)`
### `GF.prototype.argmaxBy(callback)`
### `Array.prototype.argmaxBy(callback)`
Insead of returning value, the function return the index of value in `GF` or `Array`. See [min-max.spl.js](./sample/min-max.spl.js)
```js
console.log(users.argMinBy('age'), users.minBy('age'));
console.log(users.argMaxBy('age'), users.maxBy('age'));
console.log(users.minBy('age', includeIndex = true));
console.log(users.maxBy('age', includeIndex = true));
console.log(users.minBy(['age', 'heigh']));
console.log(users.maxBy(['age', 'heigh']));
```
### `GF.prototype.argMin()`
### `Array.prototype.argMin()`
### `GF.prototype.argMax()`
### `Array.prototype.argMax()`
Special functions of `argMinBy` and `argMaxBy`. Not apply `callback` for item.
### `GF.prototype.orderBy(callback, reverse = false)`
### `Array.prototype.orderBy(callback, reverse = false)`
### `GF.prototype.sortBy(callback, reverse = false)`
### `Array.prototype.sortBy(callback, reverse = false)`
Order the item by `callback` same as [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort). The `callback` is not a comparator function, it is `GetCallback`, and we use `<`, `>`, `==` for compare. We write compare to compare 2 array by dictionary compare.
```js
const users = [
{ user: 'barney', age: 36, active: true, heigh: 10 },
{ user: 'barney', age: 46, active: true, heigh: 12 },
{ user: 'fred', age: 40, active: false, heigh: 11 },
{ user: 'barney', age: 40, active: true, heigh: 12 }
];
//order by `age`
users.orderBy('age');
// [
// { user: 'barney', age: 36, active: true, heigh: 10 },
// { user: 'fred', age: 40, active: false, heigh: 11 },
// { user: 'barney', age: 40, active: true, heigh: 12 },
// { user: 'barney', age: 46, active: true, heigh: 12 }
// ]
//order by `heigh`, if order by `age`
users.orderBy(['heigh','age']);
// [
// { user: 'barney', age: 36, active: true, heigh: 10 },
// { user: 'fred', age: 40, active: false, heigh: 11 },
// { user: 'barney', age: 40, active: true, heigh: 12 },
// { user: 'barney', age: 46, active: true, heigh: 12 }
// ]
```
See [orderby.spl.js](./sample/orderby.spl.js) for more samples.
### `GF.prototype.someBy(callback)`
### `Array.prototype.someBy(callback)`
Apply [`Array.prototype.some(callback)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) to GF.
```js
function *generator(){
yield 1;
yield 2;
yield 5;
yield 7;
};
let g = generator();
let has5 = g.someBy(x=>x == 5); //true
let has7 = g.someBy(x=>x == 7); //true
let has6 = g.someBy(x=>x == 6); //false
```
### `GF.prototype.everyBy(callback)`
### `Array.prototype.everyBy(callback)`
Apply [`Array.prototype.every(callback)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every) to GF.
```js
function *generator(){
yield 2;
yield 4;
yield 6;
yield 8;
};
let g = generator();
let even = g.everyBy(x=>x%2==0); //true
let mod3 = g.someBy(x=>x%3==0); //false
```
### `GF.prototype.irepeat(number=undefined)`
### `Array.prototype.irepeat(number=undefined)`
### `GF.prototype.repeat(number=undefined)`
### `Array.prototype.repeat(number=undefined)`
Repeats all items in this `number` of times. If the `number` is `undefined`, repeat infinite.
Eg:
```js
for(let item of [1,2].repeat(5)){
console.log(item);
}
//infinite loop
for(let item of [1,2].repeat()){
console.log(item);
}
```
### `GF.prototype.islice(begin, [end])`
### `Array.prototype.islice(begin, [end])`
### `GF.prototype.slice(begin, [end])`
### `Array.prototype.slice(begin, [end])`
Same as [`Array.prototype.slice(begin, [end])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice). See [skip-take.spl.js](./sample/skip-take.spl.js)
### `GF.prototype.itake(number)`
### `Array.prototype.itake(number)`
### `GF.prototype.take(number)`
### `Array.prototype.take(number)`
Equal to `Array.prototype.slice(0, number)`. See [skip-take.spl.js](./sample/skip-take.spl.js)
### `GF.prototype.iskip(number)`
### `Array.prototype.iskip(number)`
### `GF.prototype.skip(number)`
### `Array.prototype.skip(number)`
Equal to `Array.prototype.slice(number)`. See [skip-take.spl.js](./sample/skip-take.spl.js)
### `GF.prototype.itakeWhile(callback)`
### `Array.prototype.itakeWhile(callback)`
### `GF.prototype.takeWhile(callback)`
### `Array.prototype.takeWhile(callback)`
Take the item while `callback(item)` is true. The `callback` is `DecissionCallback`, same as `filterBy` method.
### `GF.prototype.idropWhile(callback)`
### `Array.prototype.idropWhile(callback)`
### `GF.prototype.dropWhile(callback)`
### `Array.prototype.dropWhile(callback)`
Drop the item while `callback(item)` is true. The `callback` is `DecissionCallback`, same as `filterBy` method.
### `GF.prototype.iassignProbs(probs)`
### `Array.prototype.iassignProbs(probs)`
### `GF.prototype.assignProbs(probs)`
### `Array.prototype.assignProbs(probs)`
Assign more probs for the current object. The `Array` or `Generetor` is array of pair<key,value> objects.
```js
const users = [
{ user: 'barney', active: true },
{ user: 'barney', active: true },
{ user: 'fred', active: false },
{ user: 'barney', active: true }
];
ages = [36,37,38,39]
heighs = [10,12,12,13]
users.assignProbs({
age:ages,
heigh:heighs
})
//same as
// Array
// .izip(users, ages, heighs)
// .forEach(([user,age,heigh])=>Object.assign(user,{age,heigh}))
// [
// { user: 'barney', age: 36, active: true, heigh: 10 },
// { user: 'barney', age: 37, active: true, heigh: 12 },
// { user: 'fred', age: 38, active: false, heigh: 12 },
// { user: 'barney', age: 39, active: true, heigh: 13 }
// ];
```
## License
[MIT](https://github.com/NodeRedis/node_redis/blob/master/LICENSE)