rubico 2.6.2 → 2.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/_internal/arrayMapPool.js +1 -1
- package/_internal/arrayMapRate.js +85 -0
- package/all.js +13 -23
- package/map.js +44 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|

|
|
3
3
|
> a shallow river in northeastern Italy, just south of Ravenna
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
[](https://codecov.io/gh/a-synchronous/rubico)
|
|
7
7
|
[](https://www.npmjs.com/package/rubico)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -26,7 +26,7 @@ pipe(numbers, [
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
# Installation
|
|
29
|
-
[Core build](https://unpkg.com/rubico/index.js) ([~
|
|
29
|
+
[Core build](https://unpkg.com/rubico/index.js) ([~9.8 kB minified and gzipped](https://unpkg.com/rubico/dist/rubico.min.js))
|
|
30
30
|
|
|
31
31
|
with `npm`
|
|
32
32
|
```bash
|
|
@@ -86,15 +86,12 @@ When you import this library, you obtain the freedom that comes from having thos
|
|
|
86
86
|
|
|
87
87
|
# Introduction
|
|
88
88
|
|
|
89
|
-
rubico is a library for async-enabled functional programming in JavaScript. The library
|
|
89
|
+
rubico is a library for async-enabled functional programming in JavaScript. The library supports a simple and composable functional style in asynchronous environments.
|
|
90
90
|
|
|
91
91
|
```javascript
|
|
92
92
|
const {
|
|
93
93
|
// compose functions
|
|
94
|
-
pipe, compose,
|
|
95
|
-
|
|
96
|
-
// handle effects
|
|
97
|
-
tap, forEach,
|
|
94
|
+
pipe, compose, tap,
|
|
98
95
|
|
|
99
96
|
// control flow
|
|
100
97
|
switchCase,
|
|
@@ -102,9 +99,12 @@ const {
|
|
|
102
99
|
// handle errors
|
|
103
100
|
tryCatch,
|
|
104
101
|
|
|
105
|
-
//
|
|
102
|
+
// compose data
|
|
106
103
|
all, assign, get, set, pick, omit,
|
|
107
104
|
|
|
105
|
+
// iterate
|
|
106
|
+
forEach,
|
|
107
|
+
|
|
108
108
|
// transform data
|
|
109
109
|
map, filter, reduce, transform, flatMap,
|
|
110
110
|
|
|
@@ -119,7 +119,7 @@ const {
|
|
|
119
119
|
} = rubico
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
With async-enabled, or [a]synchronous, functional programming, functions provided to the rubico
|
|
122
|
+
With async-enabled, or [a]synchronous, functional programming, functions provided to the rubico operators may be asynchronous and return a Promise. Any promises in argument position are also resolved before continuing with the operation.
|
|
123
123
|
|
|
124
124
|
```javascript [playground]
|
|
125
125
|
const helloPromise = Promise.resolve('hello')
|
|
@@ -133,7 +133,7 @@ pipe(helloPromise, [ // helloPromise is resolved for 'hello'
|
|
|
133
133
|
])
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
All rubico operators support both an eager and a lazy API. The eager API takes all required arguments and executes at once, while its lazy API takes only the non-data arguments and executes lazily, returning a function that expects the data arguments. This dual API supports a natural and composable code style.
|
|
137
137
|
|
|
138
138
|
```javascript [playground]
|
|
139
139
|
const myObj = { a: 1, b: 2, c: 3 }
|
|
@@ -150,7 +150,7 @@ console.log(myDuplicatedSquaredObject)
|
|
|
150
150
|
// { a: [1, 1], b: [4, 4], c: [9, 9] }
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
-
The rubico
|
|
153
|
+
The rubico operators are versatile and act on a wide range of vanilla JavaScript types to create declarative, extensible, and async-enabled function compositions. The same operator `map` can act on an array and also a `Map` data structure.
|
|
154
154
|
|
|
155
155
|
```javascript [playground]
|
|
156
156
|
const { pipe, tap, map, filter } = rubico
|
|
@@ -203,7 +203,7 @@ pipe(todoIDs, [
|
|
|
203
203
|
])
|
|
204
204
|
```
|
|
205
205
|
|
|
206
|
-
rubico offers transducers in its `Transducer` module. You can consume these transducers with the `transform` and `compose`
|
|
206
|
+
rubico offers transducers in its `Transducer` module. You can consume these transducers with the `transform` and `compose` operators. You should use `compose` over `pipe` to chain a left-to-right composition of transducers.
|
|
207
207
|
|
|
208
208
|
```javascript [playground]
|
|
209
209
|
const isOdd = number => number % 2 == 1
|
|
@@ -227,12 +227,12 @@ pipe(generateNumbers(), [
|
|
|
227
227
|
])
|
|
228
228
|
```
|
|
229
229
|
|
|
230
|
-
For advanced asynchronous use cases, some of the
|
|
230
|
+
For advanced asynchronous use cases, some of the operators have property functions that have different asynchronous behavior, e.g.
|
|
231
231
|
* `map` - apply a mapper function concurrently
|
|
232
232
|
* `map.pool` - apply a mapper function concurrently with a concurrency limit
|
|
233
233
|
* `map.series` - apply a mapper function serially
|
|
234
234
|
|
|
235
|
-
For more functions beyond the core
|
|
235
|
+
For more functions beyond the core operators, please visit `rubico/x`. You can find the full documentation at [rubico.land/docs](https://rubico.land/docs).
|
|
236
236
|
|
|
237
237
|
# Benchmarks
|
|
238
238
|
Please find the published benchmark output inside the [benchmark-output](https://github.com/a-synchronous/rubico/tree/master/benchmark-output) folder. You can run the benchmarks on your own system with the following command:
|
|
@@ -48,7 +48,7 @@ const arrayMapPoolAsync = async function (
|
|
|
48
48
|
*
|
|
49
49
|
* @synopsis
|
|
50
50
|
* ```coffeescript [specscript]
|
|
51
|
-
* arrayMapPool(array Array, concurrency number, f function) -> Promise|
|
|
51
|
+
* arrayMapPool(array Array, concurrency number, f function) -> Promise|array
|
|
52
52
|
* ```
|
|
53
53
|
*
|
|
54
54
|
* @description
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const curry3 = require('./curry3')
|
|
2
|
+
const __ = require('./placeholder')
|
|
3
|
+
const isPromise = require('./isPromise')
|
|
4
|
+
const promiseAll = require('./promiseAll')
|
|
5
|
+
const sleep = require('./sleep')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @name arrayMapRate
|
|
9
|
+
*
|
|
10
|
+
* @synopsis
|
|
11
|
+
* ```coffeescript [specscript]
|
|
12
|
+
* arrayMapRate(
|
|
13
|
+
* array Array,
|
|
14
|
+
* rate number,
|
|
15
|
+
* f function,
|
|
16
|
+
* ) -> Promise<Array>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
const arrayMapRate = async function (array, rate, f) {
|
|
20
|
+
const length = array.length
|
|
21
|
+
const result = Array(length)
|
|
22
|
+
const minPeriodMs = 1 / rate * 1000
|
|
23
|
+
let index = -1
|
|
24
|
+
let lastExecutionTime = 0
|
|
25
|
+
const totalStart = performance.now()
|
|
26
|
+
while (++index < length) {
|
|
27
|
+
if (index > 0 && lastExecutionTime < minPeriodMs) {
|
|
28
|
+
await sleep(minPeriodMs - lastExecutionTime)
|
|
29
|
+
}
|
|
30
|
+
const start = performance.now()
|
|
31
|
+
let resultItem = f(array[index], index, array)
|
|
32
|
+
if (isPromise(resultItem)) {
|
|
33
|
+
resultItem = await resultItem
|
|
34
|
+
}
|
|
35
|
+
const end = performance.now()
|
|
36
|
+
const executionTime = end - start
|
|
37
|
+
lastExecutionTime = executionTime
|
|
38
|
+
result[index] = resultItem
|
|
39
|
+
}
|
|
40
|
+
const totalEnd = performance.now()
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @name range
|
|
46
|
+
*
|
|
47
|
+
* @synopsis
|
|
48
|
+
* ```coffeescript [specscript]
|
|
49
|
+
* range(lower number, upper number) -> numbers Array<number>
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
const range = function (lower, upper) {
|
|
53
|
+
const result = []
|
|
54
|
+
let start = lower
|
|
55
|
+
while (start <= upper) {
|
|
56
|
+
result.push(start)
|
|
57
|
+
start += 1
|
|
58
|
+
}
|
|
59
|
+
return result
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
const start = performance.now()
|
|
64
|
+
arrayMapRate(
|
|
65
|
+
range(0, 10),
|
|
66
|
+
2,
|
|
67
|
+
async n => {
|
|
68
|
+
const duration = Math.random() * 1000
|
|
69
|
+
console.log('duration', duration)
|
|
70
|
+
// const duration = 500
|
|
71
|
+
await sleep(duration)
|
|
72
|
+
return n ** 2
|
|
73
|
+
}
|
|
74
|
+
async n => {
|
|
75
|
+
console.log('n', n)
|
|
76
|
+
return n ** 2
|
|
77
|
+
},
|
|
78
|
+
).then(result => {
|
|
79
|
+
const end = performance.now()
|
|
80
|
+
console.log('average period:', (end - start) / result.length)
|
|
81
|
+
console.log(result)
|
|
82
|
+
})
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
module.exports = arrayMapRate
|
package/all.js
CHANGED
|
@@ -113,12 +113,22 @@ const _allValues = function (values) {
|
|
|
113
113
|
* getAndLogUserById('1') // Got user {"_id":1,"name":"George"} by id 1
|
|
114
114
|
* ```
|
|
115
115
|
*
|
|
116
|
-
* Values
|
|
116
|
+
* Values may be provided along with functions, in which case they are set on the result object or array directly. If any of these values are promises, they are resolved for their values before being set on the result object or array.
|
|
117
117
|
*
|
|
118
118
|
* ```javascript [playground]
|
|
119
|
-
* all({},
|
|
119
|
+
* all({}, {
|
|
120
|
+
* a: Promise.resolve(1),
|
|
121
|
+
* b: 2,
|
|
122
|
+
* c: () => 3,
|
|
123
|
+
* d: async () => 4,
|
|
124
|
+
* }).then(console.log) // { a: 1, b: 2, c: 3, d: 4 }
|
|
125
|
+
*
|
|
126
|
+
* all([], [
|
|
120
127
|
* Promise.resolve(1),
|
|
121
|
-
*
|
|
128
|
+
* 2,
|
|
129
|
+
* () => 3,
|
|
130
|
+
* async () => 4,
|
|
131
|
+
* ]).then(console.log) // [1, 2, 3, 4]
|
|
122
132
|
* ```
|
|
123
133
|
*
|
|
124
134
|
* Any promises passed in argument position are resolved for their values before further execution. This only applies to the eager version of the API.
|
|
@@ -161,26 +171,6 @@ const all = function (...args) {
|
|
|
161
171
|
return isArray(resolversOrValues)
|
|
162
172
|
? functionArrayAll(resolversOrValues, argValues)
|
|
163
173
|
: functionObjectAll(resolversOrValues, argValues)
|
|
164
|
-
|
|
165
|
-
/*
|
|
166
|
-
////////////////////////////////////////////////////////////////
|
|
167
|
-
const funcs = args.pop()
|
|
168
|
-
if (args.length == 0) {
|
|
169
|
-
return isArray(funcs)
|
|
170
|
-
? curryArgs2(functionArrayAll, funcs, __)
|
|
171
|
-
: curryArgs2(functionObjectAll, funcs, __)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (areAnyValuesPromises(args)) {
|
|
175
|
-
return isArray(funcs)
|
|
176
|
-
? promiseAll(args).then(curry2(functionArrayAll, funcs, __))
|
|
177
|
-
: promiseAll(args).then(curry2(functionObjectAll, funcs, __))
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return isArray(funcs)
|
|
181
|
-
? functionArrayAll(funcs, args)
|
|
182
|
-
: functionObjectAll(funcs, args)
|
|
183
|
-
*/
|
|
184
174
|
}
|
|
185
175
|
|
|
186
176
|
/**
|
package/map.js
CHANGED
|
@@ -53,39 +53,39 @@ const symbolAsyncIterator = require('./_internal/symbolAsyncIterator')
|
|
|
53
53
|
* ```
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
|
-
const _map = function (value,
|
|
56
|
+
const _map = function (value, f) {
|
|
57
57
|
if (isArray(value)) {
|
|
58
|
-
return arrayMap(value,
|
|
58
|
+
return arrayMap(value, f)
|
|
59
59
|
}
|
|
60
60
|
if (value == null) {
|
|
61
61
|
return value
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (typeof value.then == 'function') {
|
|
65
|
-
return value.then(
|
|
65
|
+
return value.then(f)
|
|
66
66
|
}
|
|
67
67
|
if (typeof value.map == 'function') {
|
|
68
|
-
return value.map(
|
|
68
|
+
return value.map(f)
|
|
69
69
|
}
|
|
70
70
|
if (typeof value == 'string' || value.constructor == String) {
|
|
71
|
-
return stringMap(value,
|
|
71
|
+
return stringMap(value, f)
|
|
72
72
|
}
|
|
73
73
|
if (value.constructor == Set) {
|
|
74
|
-
return setMap(value,
|
|
74
|
+
return setMap(value, f)
|
|
75
75
|
}
|
|
76
76
|
if (value.constructor == Map) {
|
|
77
|
-
return mapMap(value,
|
|
77
|
+
return mapMap(value, f)
|
|
78
78
|
}
|
|
79
79
|
if (typeof value[symbolIterator] == 'function') {
|
|
80
|
-
return MappingIterator(value[symbolIterator](),
|
|
80
|
+
return MappingIterator(value[symbolIterator](), f)
|
|
81
81
|
}
|
|
82
82
|
if (typeof value[symbolAsyncIterator] == 'function') {
|
|
83
|
-
return MappingAsyncIterator(value[symbolAsyncIterator](),
|
|
83
|
+
return MappingAsyncIterator(value[symbolAsyncIterator](), f)
|
|
84
84
|
}
|
|
85
85
|
if (value.constructor == Object) {
|
|
86
|
-
return objectMap(value,
|
|
86
|
+
return objectMap(value, f)
|
|
87
87
|
}
|
|
88
|
-
return
|
|
88
|
+
return f(value)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
@@ -106,7 +106,7 @@ const _map = function (value, mapper) {
|
|
|
106
106
|
* ```
|
|
107
107
|
*
|
|
108
108
|
* @description
|
|
109
|
-
* Applies a synchronous or asynchronous mapper function concurrently to each item of a collection, returning the results in a new collection of the same type. If order is implied by the collection, it is maintained in the result. `map` accepts the following collections:
|
|
109
|
+
* Applies a synchronous or asynchronous mapper function `f` concurrently to each item of a collection, returning the results in a new collection of the same type. If order is implied by the collection, it is maintained in the result. `map` accepts the following collections:
|
|
110
110
|
*
|
|
111
111
|
* * `Array`
|
|
112
112
|
* * `Object`
|
|
@@ -115,7 +115,7 @@ const _map = function (value, mapper) {
|
|
|
115
115
|
* * `Iterator`/`Generator`
|
|
116
116
|
* * `AsyncIterator`/`AsyncGenerator`
|
|
117
117
|
*
|
|
118
|
-
* With arrays (type `Array`), `map` applies the mapper function to each item of the array, returning the transformed results in a new array ordered the same as the original array.
|
|
118
|
+
* With arrays (type `Array`), `map` applies the mapper function `f` to each item of the array, returning the transformed results in a new array ordered the same as the original array.
|
|
119
119
|
*
|
|
120
120
|
* ```javascript [playground]
|
|
121
121
|
* const square = number => number ** 2
|
|
@@ -131,7 +131,7 @@ const _map = function (value, mapper) {
|
|
|
131
131
|
* ) // [1, 4, 9, 16, 25]
|
|
132
132
|
* ```
|
|
133
133
|
*
|
|
134
|
-
* With objects (type `Object`), `map` applies the mapper function to each value of the object, returning the transformed results as values in a new object ordered by the keys of the original object
|
|
134
|
+
* With objects (type `Object`), `map` applies the mapper function `f` to each value of the object, returning the transformed results as values in a new object ordered by the keys of the original object
|
|
135
135
|
*
|
|
136
136
|
* ```javascript [playground]
|
|
137
137
|
* const square = number => number ** 2
|
|
@@ -147,7 +147,7 @@ const _map = function (value, mapper) {
|
|
|
147
147
|
* ) // { a: 1, b: 4, c: 9, d: 16, e: 25 }
|
|
148
148
|
* ```
|
|
149
149
|
*
|
|
150
|
-
* With sets (type `Set`), `map` applies the mapper function to each value of the set, returning the transformed results unordered in a new set.
|
|
150
|
+
* With sets (type `Set`), `map` applies the mapper function `f` to each value of the set, returning the transformed results unordered in a new set.
|
|
151
151
|
*
|
|
152
152
|
* ```javascript [playground]
|
|
153
153
|
* const square = number => number ** 2
|
|
@@ -163,7 +163,7 @@ const _map = function (value, mapper) {
|
|
|
163
163
|
* ) // [1, 4, 9, 16, 25]
|
|
164
164
|
* ```
|
|
165
165
|
*
|
|
166
|
-
* With maps (type `Map`), `map` applies the mapper function to each value of the map, returning the results at the same keys in a new map. The entries of the resulting map are in the same order as those of the original map
|
|
166
|
+
* With maps (type `Map`), `map` applies the mapper function `f` to each value of the map, returning the results at the same keys in a new map. The entries of the resulting map are in the same order as those of the original map
|
|
167
167
|
*
|
|
168
168
|
* ```javascript [playground]
|
|
169
169
|
* const square = number => number ** 2
|
|
@@ -179,7 +179,7 @@ const _map = function (value, mapper) {
|
|
|
179
179
|
* ) // Map { 'a' => 1, 'b' => 4, 'c' => 9, 'd' => 16, 'e' => 25 }
|
|
180
180
|
* ```
|
|
181
181
|
*
|
|
182
|
-
* With iterators (type `Iterator`) or generators (type `Generator`), `map` applies the mapper function lazily to each value of the iterator/generator, creating a new iterator with transformed iterations.
|
|
182
|
+
* With iterators (type `Iterator`) or generators (type `Generator`), `map` applies the mapper function `f` lazily to each value of the iterator/generator, creating a new iterator with transformed iterations.
|
|
183
183
|
*
|
|
184
184
|
* ```javascript [playground]
|
|
185
185
|
* const capitalize = string => string.toUpperCase()
|
|
@@ -199,7 +199,7 @@ const _map = function (value, mapper) {
|
|
|
199
199
|
* console.log([...ABCGenerator2]) // ['A', 'B', 'C']
|
|
200
200
|
* ```
|
|
201
201
|
*
|
|
202
|
-
* With asyncIterators (type `AsyncIterator`, or `AsyncGenerator`), `map` applies the mapper function lazily to each value of the asyncIterator, creating a new asyncIterator with transformed iterations
|
|
202
|
+
* With asyncIterators (type `AsyncIterator`, or `AsyncGenerator`), `map` applies the mapper function `f` lazily to each value of the asyncIterator, creating a new asyncIterator with transformed iterations
|
|
203
203
|
*
|
|
204
204
|
* ```javascript [playground]
|
|
205
205
|
* const capitalize = string => string.toUpperCase()
|
|
@@ -258,16 +258,16 @@ const map = function (arg0, arg1) {
|
|
|
258
258
|
: _map(arg0, arg1)
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
// _mapEntries(value Object|Map,
|
|
262
|
-
const _mapEntries = (value,
|
|
261
|
+
// _mapEntries(value Object|Map, f function) -> Object|Map
|
|
262
|
+
const _mapEntries = (value, f) => {
|
|
263
263
|
if (value == null) {
|
|
264
264
|
throw new TypeError('value is not an Object or Map')
|
|
265
265
|
}
|
|
266
266
|
if (value.constructor == Object) {
|
|
267
|
-
return objectMapEntries(value,
|
|
267
|
+
return objectMapEntries(value, f)
|
|
268
268
|
}
|
|
269
269
|
if (value.constructor == Map) {
|
|
270
|
-
return mapMapEntries(value,
|
|
270
|
+
return mapMapEntries(value, f)
|
|
271
271
|
}
|
|
272
272
|
throw new TypeError('value is not an Object or Map')
|
|
273
273
|
}
|
|
@@ -285,10 +285,10 @@ const _mapEntries = (value, mapper) => {
|
|
|
285
285
|
* collection EntriesMappable
|
|
286
286
|
* )=>(resultItem Promise|any)
|
|
287
287
|
*
|
|
288
|
-
* map.entries(value Promise|EntriesMappable,
|
|
288
|
+
* map.entries(value Promise|EntriesMappable, f Mapper)
|
|
289
289
|
* -> Promise|EntriesMappable
|
|
290
290
|
*
|
|
291
|
-
* map.entries(
|
|
291
|
+
* map.entries(f Mapper)(value EntriesMappable)
|
|
292
292
|
* -> Promise|EntriesMappable
|
|
293
293
|
* ```
|
|
294
294
|
*
|
|
@@ -512,4 +512,24 @@ map.pool = function mapPool(arg0, arg1, arg2) {
|
|
|
512
512
|
: _mapPool(arg0, arg1, arg2)
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
+
/**
|
|
516
|
+
* @name map.rate
|
|
517
|
+
*
|
|
518
|
+
* @synopsis
|
|
519
|
+
* ```coffeescript [specscript]
|
|
520
|
+
* type Mappable = Array|Object|Set|Map
|
|
521
|
+
*
|
|
522
|
+
* map.rate(
|
|
523
|
+
* rate number,
|
|
524
|
+
* f (value any)=>Promise|any,
|
|
525
|
+
* )(collection Mappable) -> result Promise|Array
|
|
526
|
+
*
|
|
527
|
+
* map.rate(
|
|
528
|
+
* collection Mappable,
|
|
529
|
+
* rate number,
|
|
530
|
+
* f (value any)=>Promise|any,
|
|
531
|
+
* ) -> result Promise|Array
|
|
532
|
+
* ```
|
|
533
|
+
*/
|
|
534
|
+
|
|
515
535
|
module.exports = map
|