jupyter-ijavascript-utils 1.21.3 → 1.22.0
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/DOCS.md +2 -0
- package/Dockerfile +1 -1
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/chain.js +361 -27
package/DOCS.md
CHANGED
|
@@ -65,6 +65,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
|
|
|
65
65
|
|
|
66
66
|
## What's New
|
|
67
67
|
|
|
68
|
+
* 1.22 - make chain iJavaScript aware, but still able to work outside of Jupyter
|
|
68
69
|
* 1.21 - include {@link module:chain|chain} - simple monoid
|
|
69
70
|
* 1.20 - fix vega dependency
|
|
70
71
|
* 1.19 - add in {@link module:describe|describe} and {@link module:hashMap|hashMap} modules, along with {@link module:format.limitLines|format.limitLines}
|
|
@@ -94,6 +95,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
|
|
|
94
95
|
| {@link module:aggregate} | Aggregate collections or collections of objects (ex: min, max, unique, contains, etc. |
|
|
95
96
|
| {@link module:array} | Massage, sort, reshape arrays. |
|
|
96
97
|
| {@link module:base64} | Convert to and from base64 encoding of strings |
|
|
98
|
+
| {@link module:chain} | Simple wrapper (Monad-ish) that allows for chaining statements together |
|
|
97
99
|
| {@link module:datasets} | Load example <a href="https://github.com/vega/vega-datasets">datasets provided by the vega team</a> |
|
|
98
100
|
| {@link module:describe} | Similar to Pandas describe, provides statistics on a set of values / objects |
|
|
99
101
|
| {@link module:file} | Read and write data/text to files. |
|
package/Dockerfile
CHANGED
package/README.md
CHANGED
|
@@ -55,6 +55,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
|
|
|
55
55
|
|
|
56
56
|
# What's New
|
|
57
57
|
|
|
58
|
+
* 1.22 - make chain iJavaScript aware, but still able to work outside of Jupyter
|
|
58
59
|
* 1.21 - include chain - simple monoid
|
|
59
60
|
* 1.20 - fix vega dependency
|
|
60
61
|
* 1.19 - add in describe and hashMap modules, along with format.limitLines
|
package/package.json
CHANGED
package/src/chain.js
CHANGED
|
@@ -1,16 +1,369 @@
|
|
|
1
|
+
/* eslint-disable class-methods-use-this */
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Simple monad like wrapper.
|
|
3
5
|
*
|
|
6
|
+
* {@link module:chain|See the util.chain method} for more.
|
|
7
|
+
*
|
|
4
8
|
* Very helpful for taking values and then progressively working on them,
|
|
5
9
|
* instead of continually wrapping deeper in method calls.
|
|
6
10
|
*
|
|
7
11
|
* Calling `chain(3)` - gives an object with two properties:
|
|
8
12
|
*
|
|
9
|
-
* *
|
|
10
|
-
* *
|
|
11
|
-
* *
|
|
12
|
-
* *
|
|
13
|
-
* *
|
|
13
|
+
* * {@link ChainContainer#close|.close()} - gets the value of the current chain
|
|
14
|
+
* * {@link ChainContainer#chain|.chain(function)} - where it is passed the value, and returns a new Chain with that value.
|
|
15
|
+
* * {@link ChainContainer#chainMap|.chainMap(function)} - where it treats value as an array, and maps function on every item in the array
|
|
16
|
+
* * {@link ChainContainer#chainReduce|.chainReduce(function, initialValue)} - where it treats value as an array, and reduces the value array
|
|
17
|
+
* * {@link ChainContainer#errorHandler|.errorHandler(fn)} - custom function called if an error is ever thrown
|
|
18
|
+
* * {@link ChainContainer#debug|.debug()} - console.logs the current value, and continues the chain with that value
|
|
19
|
+
*/
|
|
20
|
+
class ChainContainer {
|
|
21
|
+
/**
|
|
22
|
+
* Custom function to run if there is any error along the chain.
|
|
23
|
+
* (Scoped to the current Container when running)
|
|
24
|
+
* @type {Function}
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
errorHandlerFn;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Value this container stores, can be anythhing.
|
|
31
|
+
*
|
|
32
|
+
* Also access through {@link ChainContainer#close}
|
|
33
|
+
*
|
|
34
|
+
* @type {any}
|
|
35
|
+
*/
|
|
36
|
+
value;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Constructor that creates a new Container to pass along the chain.
|
|
40
|
+
*
|
|
41
|
+
* If the context is null, then we assume we are binding the container to the current iJavaScript cell.
|
|
42
|
+
*
|
|
43
|
+
* @param {any} value - the value to use along the chain
|
|
44
|
+
*/
|
|
45
|
+
constructor(value) {
|
|
46
|
+
this.value = value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Clones the value, and ties the output to this cell.
|
|
51
|
+
* @returns {ChainContainer} - new chain container to use in this cell.
|
|
52
|
+
* @example
|
|
53
|
+
*
|
|
54
|
+
* customErrorHandler = (err) => console.error('Some custom warning');
|
|
55
|
+
* initialChain = utils.chain(3)
|
|
56
|
+
* .errorHandler(customErrorHandler);
|
|
57
|
+
*
|
|
58
|
+
* newChain = initialChain.clone()
|
|
59
|
+
* .chain(v => v + 2) // 3 + 2
|
|
60
|
+
* .close();
|
|
61
|
+
*
|
|
62
|
+
* // 5
|
|
63
|
+
*/
|
|
64
|
+
clone() {
|
|
65
|
+
const result = new ChainContainer(this.value);
|
|
66
|
+
result.errorHandlerFn = this.errorHandlerFn;
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates a new chain container holding the value returned from functor(this.value)
|
|
72
|
+
*
|
|
73
|
+
* ```
|
|
74
|
+
* value = 2;
|
|
75
|
+
* plus2 = (value) => value + 2;
|
|
76
|
+
*
|
|
77
|
+
* chain(value)
|
|
78
|
+
* .chain(plus2) // 2 + 2
|
|
79
|
+
* .chain(plus2) // 4 + 2
|
|
80
|
+
* .debug()
|
|
81
|
+
* .chain(plu2) // 6 + 2
|
|
82
|
+
* .close();
|
|
83
|
+
*
|
|
84
|
+
* // 6
|
|
85
|
+
* // 8
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Or even combine with other utility methods
|
|
89
|
+
*
|
|
90
|
+
* ```
|
|
91
|
+
* badStr = 'I%20am%20the%20very%20model%20of%20a%20modern%20Major'
|
|
92
|
+
* + '-General%0AI\'ve%20information%20vegetable%2C%20animal%2C%20'
|
|
93
|
+
* + 'and%20mineral%0AI%20know%20the%20kings%20of%20England%2C%20'
|
|
94
|
+
* + 'and%20I%20quote%20the%20fights%0AHistorical%0AFrom%20Marath'
|
|
95
|
+
* + 'on%20to%20Waterloo%2C%20in%20order%20categorical';
|
|
96
|
+
*
|
|
97
|
+
* chain(badStr)
|
|
98
|
+
* .chain(decodeURIComponent)
|
|
99
|
+
* .chain(v => v.split('\n'))
|
|
100
|
+
* // .debug() // check the values along the way
|
|
101
|
+
* .chainMap(line => ({ line, length: line.length }))
|
|
102
|
+
* .chain(values => utils.table(values).render());
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* and it renders out a lovely table like this:
|
|
106
|
+
*
|
|
107
|
+
* line |length
|
|
108
|
+
* -- |--
|
|
109
|
+
* I am the very model of a modern Major-General |45
|
|
110
|
+
* I've information vegetable, animal, and mineral |47
|
|
111
|
+
* I know the kings of England, and I quote the fights|51
|
|
112
|
+
* Historical |10
|
|
113
|
+
* From Marathon to Waterloo, in order categorical |47
|
|
114
|
+
*
|
|
115
|
+
* @param {Function} functor - the function given the value, returning a transformed value
|
|
116
|
+
* @see {@link ChainContainer#chain} - to get the value
|
|
117
|
+
* @see {@link ChainContainer#chainMap} - convenience to apply a function to each item in an array
|
|
118
|
+
* @see {@link ChainContainer#chainReduce} - convenience to reduce the array
|
|
119
|
+
* @see {@link ChainContainer#debug} - to see the value at a specific time
|
|
120
|
+
* @returns {ChainContainer} - container with the results from functor(this.value)
|
|
121
|
+
*/
|
|
122
|
+
chain(functor) {
|
|
123
|
+
try {
|
|
124
|
+
return this.update(functor(this.value));
|
|
125
|
+
} catch (err) {
|
|
126
|
+
this.handleError(err);
|
|
127
|
+
|
|
128
|
+
//-- handle error throws again, line will never be called.
|
|
129
|
+
// this.close();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Assuming that value is an array, this maps fn to every value in the array.
|
|
135
|
+
*
|
|
136
|
+
* ```
|
|
137
|
+
* initializeArray = (size) => Array.from(Array(size)).map((val, index) => index);
|
|
138
|
+
* initializeArray(3); // [0, 1, 2]
|
|
139
|
+
*
|
|
140
|
+
* addTwo = (value) => value + 2;
|
|
141
|
+
* addTwo(3); // 5
|
|
142
|
+
*
|
|
143
|
+
* utils.chain(3)
|
|
144
|
+
* .chain(initializeArray) // [0, 1, 2]
|
|
145
|
+
* .chainMap(addTwo) // [2, 3, 4] or [0 + 2, 1 + 2, 2 + 2]
|
|
146
|
+
* .chainMap(addTwo)
|
|
147
|
+
* .close();
|
|
148
|
+
*
|
|
149
|
+
* // [4, 5, 6]
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @param {Function} fn - applies function under every index of this.value
|
|
153
|
+
* @returns {ChainContainer}
|
|
154
|
+
*/
|
|
155
|
+
chainMap(fn) {
|
|
156
|
+
if (!Array.isArray(this.value)) throw Error(`chainMap expected an array, but was passed:${this.value}`);
|
|
157
|
+
|
|
158
|
+
return this.chain((value) => value.map(fn));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Assuming that the value is an array, performs a reduce using fn and initialValue
|
|
163
|
+
*
|
|
164
|
+
* ```
|
|
165
|
+
* initializeArray = (size) => Array.from(Array(size)).map((val, index) => index);
|
|
166
|
+
* initializeArray(3); // [0, 1, 2]
|
|
167
|
+
*
|
|
168
|
+
* addTwo = (value) => value + 2;
|
|
169
|
+
* addTwo(3); // 5
|
|
170
|
+
*
|
|
171
|
+
* utils.chain(3)
|
|
172
|
+
* .chain(initializeArray) // [0, 1, 2]
|
|
173
|
+
* .chainMap(addTwo) // [2, 3, 4] or [0 + 2, 1 + 2, 2 + 2]
|
|
174
|
+
* .debug()
|
|
175
|
+
* .chainReduce((result, value) => result + value, 0)
|
|
176
|
+
* .close();
|
|
177
|
+
*
|
|
178
|
+
* // [2, 3, 4]
|
|
179
|
+
* // 9
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @param {Function} fn - reducer function
|
|
183
|
+
* @param {any} initialValue - initial value passed to the reducer
|
|
184
|
+
* @returns {ChainContainer}
|
|
185
|
+
*/
|
|
186
|
+
chainReduce(fn, initialValue) {
|
|
187
|
+
if (!Array.isArray(this.value)) throw Error(`chainReduce expected an array, but was passed:${this.value}`);
|
|
188
|
+
|
|
189
|
+
return this.chain((value) => value.reduce(fn, initialValue));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Console.logs the value, and returns the unmodified current value.
|
|
194
|
+
*
|
|
195
|
+
* ```
|
|
196
|
+
* value = 2;
|
|
197
|
+
* plus2 = (value) => value + 2;
|
|
198
|
+
*
|
|
199
|
+
* chain(value)
|
|
200
|
+
* .chain(plus2) // 2 + 2
|
|
201
|
+
* .chain(plus2) // 4 + 2
|
|
202
|
+
* .debug()
|
|
203
|
+
* .chain(plu2) // 6 + 2
|
|
204
|
+
* .debug((value) => console.log(value))
|
|
205
|
+
* .close();
|
|
206
|
+
*
|
|
207
|
+
* // 6
|
|
208
|
+
* // 8
|
|
209
|
+
* // 8
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* @param {Function} [fn=null] - optional custom function
|
|
213
|
+
* @returns {ChainContainer} - the same value as current, regardless of the result from fn.
|
|
214
|
+
*/
|
|
215
|
+
debug(fn) {
|
|
216
|
+
if (fn) {
|
|
217
|
+
fn(this.value);
|
|
218
|
+
} else {
|
|
219
|
+
this.console(this.value);
|
|
220
|
+
}
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Function to call if an error occurs anywhere on the chain.
|
|
226
|
+
*
|
|
227
|
+
* ```
|
|
228
|
+
* someErrorOccurred = false;
|
|
229
|
+
* flipSwitch = (err) => {
|
|
230
|
+
* console.log('custom handler');
|
|
231
|
+
* someErrorOccurred = true;
|
|
232
|
+
* };
|
|
233
|
+
* throwError = () => {
|
|
234
|
+
* throw Error('Custom Error');
|
|
235
|
+
* };
|
|
236
|
+
*
|
|
237
|
+
* chain(2)
|
|
238
|
+
* .errorHandler(flipSwitch)
|
|
239
|
+
* .chain((value) => value + 2)
|
|
240
|
+
* .chain(throwError);
|
|
241
|
+
*
|
|
242
|
+
* // customHandler
|
|
243
|
+
* // {
|
|
244
|
+
* // "name": "Error",
|
|
245
|
+
* // "message": "Custom Error"
|
|
246
|
+
* // }
|
|
247
|
+
* // /Users/proth/Documents/notebooks/jupyter-ijavascript-utils/src/chain.js:175
|
|
248
|
+
* // throw err;
|
|
249
|
+
* // ^
|
|
250
|
+
* //
|
|
251
|
+
* // Error: Custom Error
|
|
252
|
+
* // at throwError (evalmachine.<anonymous>:7:9)
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* @param {Function} errorHandler - function that is passed the error caught
|
|
256
|
+
*/
|
|
257
|
+
errorHandler(errorHandlerFn) {
|
|
258
|
+
this.errorHandlerFn = errorHandlerFn;
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Closes the chain and returns the current value.
|
|
264
|
+
* @returns {any}
|
|
265
|
+
* @see {@link ChainContainer#chain}
|
|
266
|
+
* @example
|
|
267
|
+
*
|
|
268
|
+
* utils.chain(3)
|
|
269
|
+
* .chain(v => v + 2) // 3 + 2
|
|
270
|
+
* .close();
|
|
271
|
+
*
|
|
272
|
+
* // 5
|
|
273
|
+
*/
|
|
274
|
+
close() {
|
|
275
|
+
return this.value;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
//-- private methods
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Clones the value, and ties the output to this cell.
|
|
282
|
+
* @param {any} newValue - new value of the clone
|
|
283
|
+
* @returns {ChainContainer} - new chain container to use in this cell.
|
|
284
|
+
* @private
|
|
285
|
+
*/
|
|
286
|
+
update(newValue) {
|
|
287
|
+
const result = new ChainContainer(newValue);
|
|
288
|
+
result.errorHandlerFn = this.errorHandlerFn;
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Default handler catching any error that happens along the chain.
|
|
294
|
+
* @param {Error} err - the error caught
|
|
295
|
+
* @private
|
|
296
|
+
*/
|
|
297
|
+
handleError(err) {
|
|
298
|
+
if (this.errorHandlerFn) {
|
|
299
|
+
this.errorHandlerFn.apply(this, [err]);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
console.error(JSON.stringify(err, ['name', 'message'], 2));
|
|
303
|
+
|
|
304
|
+
// Assert.fail('error occurred. halting.');
|
|
305
|
+
throw err;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* call the console regardless of context
|
|
310
|
+
* @param {any} msg - message to send
|
|
311
|
+
* @private
|
|
312
|
+
*/
|
|
313
|
+
console(msg) {
|
|
314
|
+
console.log(msg); // eslint-disable-line no-console
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* toString replacement
|
|
319
|
+
* @private
|
|
320
|
+
*/
|
|
321
|
+
toString() {
|
|
322
|
+
const { context, ...cleanThis } = this; // eslint-disable-line
|
|
323
|
+
return JSON.stringify(cleanThis, 2, false);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* inspect replacement
|
|
328
|
+
* @private
|
|
329
|
+
*/
|
|
330
|
+
inspect() {
|
|
331
|
+
return this.toString();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* toJSON replacement
|
|
336
|
+
* @private
|
|
337
|
+
*/
|
|
338
|
+
toJSON() {
|
|
339
|
+
const { context, ...cleanThis } = this; // eslint-disable-line
|
|
340
|
+
return cleanThis;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* toStringTag used for inspect
|
|
345
|
+
*/
|
|
346
|
+
// get [Symbol.toStringTag]() {
|
|
347
|
+
// return 'Chain';
|
|
348
|
+
// }
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Simple monad like wrapper.
|
|
353
|
+
*
|
|
354
|
+
* {@link ChainContainer|See the ChainContainer} for more.
|
|
355
|
+
*
|
|
356
|
+
* Very helpful for taking values and then progressively working on them,
|
|
357
|
+
* instead of continually wrapping deeper in method calls.
|
|
358
|
+
*
|
|
359
|
+
* Calling `chain(3)` - gives an object you can either use the value of, or continue the chain going.
|
|
360
|
+
*
|
|
361
|
+
* * {@link ChainContainer#close|.close()} - gets the value of the current chain
|
|
362
|
+
* * {@link ChainContainer#chain|.chain(function)} - where it is passed the value, and returns a new Chain with that value.
|
|
363
|
+
* * {@link ChainContainer#chainMap|.chainMap(function)} - where it treats value as an array, and maps function on every item in the array
|
|
364
|
+
* * {@link ChainContainer#chainReduce|.chainReduce(function, initialValue)} - where it treats value as an array, and reduces the value array
|
|
365
|
+
* * {@link ChainContainer#errorHandler|.errorHandler(fn)} - custom function called if an error is ever thrown
|
|
366
|
+
* * {@link ChainContainer#debug|.debug()} - console.logs the current value, and continues the chain with that value
|
|
14
367
|
*
|
|
15
368
|
* For example:
|
|
16
369
|
*
|
|
@@ -91,26 +444,7 @@
|
|
|
91
444
|
* @module chain
|
|
92
445
|
*/
|
|
93
446
|
module.exports = function chain(value) {
|
|
94
|
-
return (
|
|
95
|
-
value,
|
|
96
|
-
chain: function innerChain(fn) {
|
|
97
|
-
return chain(fn(value));
|
|
98
|
-
},
|
|
99
|
-
chainMap: function chainMap(fn) {
|
|
100
|
-
if (!Array.isArray(value)) throw Error(`chainMap expected an array, but was passed:${value}`);
|
|
101
|
-
return chain(value.map(fn));
|
|
102
|
-
},
|
|
103
|
-
chainReduce: function chainReduce(fn, initialValue) {
|
|
104
|
-
if (!Array.isArray(value)) throw Error(`chainReduce expected an array, but was passed:${value}`);
|
|
105
|
-
return chain(value.reduce(fn, initialValue));
|
|
106
|
-
},
|
|
107
|
-
debug: function debug(fn) {
|
|
108
|
-
if (fn) {
|
|
109
|
-
fn(value);
|
|
110
|
-
} else {
|
|
111
|
-
console.log(value);
|
|
112
|
-
}
|
|
113
|
-
return chain(value);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
447
|
+
return new ChainContainer(value);
|
|
116
448
|
};
|
|
449
|
+
|
|
450
|
+
// example gist: https://gist.github.com/paulroth3d/fc97580636ba706783c6a84467a625db
|