README.md (9491B)
1 <!-- 2 3 @license Apache-2.0 4 5 Copyright (c) 2018 The Stdlib Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 19 --> 20 21 # tabulateByAsync 22 23 > Generate a frequency table according to an indicator function. 24 25 <!-- Section to include introductory text. Make sure to keep an empty line after the intro `section` element and another before the `/section` close. --> 26 27 <section class="intro"> 28 29 </section> 30 31 <!-- /.intro --> 32 33 <!-- Package usage documentation. --> 34 35 <section class="usage"> 36 37 ## Usage 38 39 ```javascript 40 var tabulateByAsync = require( '@stdlib/utils/async/tabulate-by' ); 41 ``` 42 43 #### tabulateByAsync( collection, \[options,] indicator, done ) 44 45 Generates a frequency table according to an `indicator` function, i.e., a function which specifies how to categorize an element in the input `collection`. 46 47 ```javascript 48 function indicator( value, next ) { 49 setTimeout( onTimeout, value ); 50 function onTimeout() { 51 console.log( value ); 52 next( null, (value > 2000) ); 53 } 54 } 55 56 function done( error, result ) { 57 if ( error ) { 58 throw error; 59 } 60 console.log( result ); 61 } 62 63 var arr = [ 3000, 2500, 1000, 750 ]; 64 65 tabulateByAsync( arr, indicator, done ); 66 /* => 67 750 68 1000 69 2500 70 3000 71 [ [ false, 2, 0.5 ], [ true, 2, 0.5 ] ] 72 */ 73 ``` 74 75 The returned frequency table is an `array` of `arrays`. Each sub-array corresponds to a unique value in the input `collection` and is structured as follows: 76 77 - `0`: unique value 78 - `1`: value count 79 - `2`: frequency percentage 80 81 The function accepts the following `options`: 82 83 - `limit`: the maximum number of pending invocations at any one time. Default: `infinity`. 84 - `series`: `boolean` indicating whether to sequentially invoke the `indicator` function for each `collection` element. If `true`, the function sets `options.limit=1`. Default: `false`. 85 - `thisArg`: the execution context for `indicator`. 86 87 By default, all elements are processed concurrently, which means that the function does **not** guarantee completion order. To process each `collection` element sequentially, set the `series` option to `true`. 88 89 ```javascript 90 function indicator( value, next ) { 91 setTimeout( onTimeout, value ); 92 function onTimeout() { 93 console.log( value ); 94 next( null, (value > 2000) ); 95 } 96 } 97 98 function done( error, result ) { 99 if ( error ) { 100 throw error; 101 } 102 console.log( result ); 103 } 104 105 var arr = [ 3000, 2500, 1000, 750 ]; 106 107 var opts = { 108 'series': true 109 }; 110 111 tabulateByAsync( arr, opts, indicator, done ); 112 /* => 113 3000 114 2500 115 1000 116 750 117 [ [ true, 2, 0.5 ], [ false, 2, 0.5 ] ] 118 */ 119 ``` 120 121 To limit the maximum number of pending function invocations, set the `limit` option. 122 123 ```javascript 124 function indicator( value, next ) { 125 setTimeout( onTimeout, value ); 126 function onTimeout() { 127 console.log( value ); 128 next( null, (value > 2000) ); 129 } 130 } 131 132 function done( error, result ) { 133 if ( error ) { 134 throw error; 135 } 136 console.log( result ); 137 } 138 139 var arr = [ 3000, 2500, 1000, 750 ]; 140 141 var opts = { 142 'limit': 2 143 }; 144 145 tabulateByAsync( arr, opts, indicator, done ); 146 /* => 147 2500 148 3000 149 1000 150 750 151 [ [ true, 2, 0.5 ], [ false, 2, 0.5 ] ] 152 */ 153 ``` 154 155 To set the execution context of the `indicator` function, set the `thisArg` option. 156 157 ```javascript 158 function indicator( value, next ) { 159 this.count += 1; 160 setTimeout( onTimeout, value ); 161 function onTimeout() { 162 next( null, (value > 2000) ); 163 } 164 } 165 166 var arr = [ 3000, 2500, 1000, 750 ]; 167 168 var context = { 169 'count': 0 170 }; 171 172 var opts = { 173 'thisArg': context 174 }; 175 176 tabulateByAsync( arr, opts, indicator, done ); 177 178 function done( error, result ) { 179 if ( error ) { 180 throw error; 181 } 182 console.log( result ); 183 // => [ [ false, 2, 0.5 ], [ true, 2, 0.5 ] ] 184 185 console.log( context.count ); 186 // => 4 187 } 188 ``` 189 190 When invoked, the `indicator` function is provided a maximum of four arguments: 191 192 - `value`: collection value. 193 - `index`: collection index. 194 - `collection`: the input `collection`. 195 - `next`: a callback which should be called once the `indicator` function has finished processing a collection `value`. 196 197 The actual number of provided arguments depends on function `length`. If the `indicator` function accepts two arguments, the `indicator` function is provided `value` and `next`. If the `indicator` function accepts three arguments, the `indicator` function is provided `value`, `index`, and `next`. For every other `indicator` function signature, the `indicator` function is provided all four arguments. 198 199 ```javascript 200 function indicator( value, i, collection, next ) { 201 console.log( 'collection: %s. %d: %d', collection.join( ',' ), i, value ); 202 setTimeout( onTimeout, value ); 203 function onTimeout() { 204 console.log( value ); 205 next( null, (value > 2000) ); 206 } 207 } 208 209 function done( error, result ) { 210 if ( error ) { 211 throw error; 212 } 213 console.log( result ); 214 } 215 216 var arr = [ 3000, 2500, 1000, 750 ]; 217 218 tabulateByAsync( arr, indicator, done ); 219 /* => 220 collection: 3000,2500,1000,750. 0: 3000 221 collection: 3000,2500,1000,750. 1: 2500 222 collection: 3000,2500,1000,750. 2: 1000 223 collection: 3000,2500,1000,750. 3: 750 224 750 225 1000 226 2500 227 3000 228 [ [ false, 2, 0.5 ], [ true, 2, 0.5 ] ] 229 */ 230 ``` 231 232 #### tabulateByAsync.factory( \[options,] indicator ) 233 234 Returns a `function` which invokes an `indicator` function once for each element in a `collection` and generates a frequency table. 235 236 ```javascript 237 function indicator( value, next ) { 238 setTimeout( onTimeout, value ); 239 function onTimeout() { 240 console.log( value ); 241 next( null, (value > 2000) ); 242 } 243 } 244 245 function done( error, result ) { 246 if ( error ) { 247 throw error; 248 } 249 console.log( result ); 250 } 251 252 var f = tabulateByAsync.factory( indicator ); 253 254 var arr1 = [ 3000, 2500, 1000, 750 ]; 255 256 f( arr1, done ); 257 /* => 258 750 259 1000 260 2500 261 3000 262 [ [ false, 2, 0.5 ], [ true, 2, 0.5 ] ] 263 */ 264 265 var arr2 = [ 300, 250, 100 ]; 266 267 f( arr2, done ); 268 /* => 269 100 270 250 271 300 272 [ [ false, 3, 1.0 ] ] 273 */ 274 ``` 275 276 The function accepts the same `options` as `tabulateByAsync()`. 277 278 </section> 279 280 <!-- /.usage --> 281 282 <!-- Package usage notes. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 283 284 <section class="notes"> 285 286 ## Notes 287 288 - A `collection` may be either an [`Array`][mdn-array], [`Typed Array`][mdn-typed-array], or an array-like [`Object`][mdn-object] (excluding `strings` and `functions`). 289 - If a provided function calls the `next` callback with a truthy `error` argument, the function suspends execution and immediately calls the `done` callback for subsequent `error` handling. 290 - The function does **not** support dynamic `collection` resizing. 291 - The function does **not** skip `undefined` elements. 292 - If provided an empty `collection`, the function calls the `done` callback with an empty `array` for the tabulated results. 293 - **Neither** `tabulateByAsync` nor the function returned by the `factory` method **guarantee** asynchronous execution. To guarantee asynchrony, wrap the `done` callback in a function which either executes at the end of the current stack (e.g., `nextTick`) or during a subsequent turn of the event loop (e.g., `setImmediate`, `setTimeout`). 294 295 </section> 296 297 <!-- /.notes --> 298 299 <!-- Package usage examples. --> 300 301 <section class="examples"> 302 303 ## Examples 304 305 <!-- eslint no-undef: "error" --> 306 307 ```javascript 308 var resolve = require( 'path' ).resolve; 309 var readFile = require( '@stdlib/fs/read-file' ); 310 var tabulateByAsync = require( '@stdlib/utils/async/tabulate-by' ); 311 312 var files = [ 313 resolve( __dirname, 'package.json' ), 314 resolve( __dirname, 'README.md' ), 315 resolve( __dirname, 'beep.boop.md' ) 316 ]; 317 318 function done( error, result ) { 319 if ( error ) { 320 throw error; 321 } 322 console.log( result ); 323 } 324 325 function indicator( file, next ) { 326 var opts = { 327 'encoding': 'utf8' 328 }; 329 readFile( file, opts, onFile ); 330 331 function onFile( error ) { 332 if ( error ) { 333 return next( null, 'nonreadable' ); 334 } 335 next( null, 'readable' ); 336 } 337 } 338 339 tabulateByAsync( files, indicator, done ); 340 ``` 341 342 </section> 343 344 <!-- /.examples --> 345 346 <!-- Section to include cited references. If references are included, add a horizontal rule *before* the section. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 347 348 <section class="references"> 349 350 </section> 351 352 <!-- /.references --> 353 354 <!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 355 356 <section class="links"> 357 358 [mdn-array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array 359 360 [mdn-typed-array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray 361 362 [mdn-object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object 363 364 </section> 365 366 <!-- /.links -->