UNPKG

gf-js

Version:

Attach most of function from lodash to Javascript GeneratorFunction and Array.

682 lines (587 loc) 21.3 kB
# 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)