README.md (11455B)
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 # groupByAsync 22 23 > Group values 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 groupByAsync = require( '@stdlib/utils/async/group-by' ); 41 ``` 42 43 #### groupByAsync( collection, \[options,] indicator, done ) 44 45 Groups values according to an `indicator` function, which specifies which group an element in the input `collection` belongs to. 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 ]; 64 65 groupByAsync( arr, indicator, done ); 66 /* => 67 1000 68 2500 69 3000 70 { 'true': [ 2500, 3000 ], 'false': [ 1000 ] } 71 */ 72 ``` 73 74 The function accepts the following `options`: 75 76 - `limit`: the maximum number of pending invocations at any one time. Default: `infinity`. 77 - `series`: `boolean` indicating whether to sequentially invoke the `indicator` function for each `collection` element. If `true`, the function sets `options.limit=1`. Default: `false`. 78 - `returns`: specifies the output format. If the option equals `'values'`, the function returns group results as element values. If the option equals `'indices'`, the function returns group results as element indices. If the option equals `'*'`, the function returns group results as both element indices and values. Default: `'values'`. 79 - `thisArg`: the execution context for `indicator`. 80 81 By default, the function returns group results as element values. To return element indices, set the `returns` option to `'indices'`. 82 83 ```javascript 84 function indicator( value, next ) { 85 setTimeout( onTimeout, value ); 86 function onTimeout() { 87 console.log( value ); 88 next( null, (value > 2000) ); 89 } 90 } 91 92 function done( error, result ) { 93 if ( error ) { 94 throw error; 95 } 96 console.log( result ); 97 } 98 99 var arr = [ 3000, 2500, 1000 ]; 100 101 var opts = { 102 'returns': 'indices' 103 }; 104 groupByAsync( arr, opts, indicator, done ); 105 /* => 106 1000 107 2500 108 3000 109 { 'true': [ 1, 0 ], 'false': [ 2 ] } 110 */ 111 ``` 112 113 To return index-value pairs, set the `returns` option to `'*'`. 114 115 ```javascript 116 function indicator( value, next ) { 117 setTimeout( onTimeout, value ); 118 function onTimeout() { 119 console.log( value ); 120 next( null, (value > 2000) ); 121 } 122 } 123 124 function done( error, result ) { 125 if ( error ) { 126 throw error; 127 } 128 console.log( result ); 129 } 130 131 var arr = [ 3000, 2500, 1000 ]; 132 133 var opts = { 134 'returns': '*' 135 }; 136 groupByAsync( arr, opts, indicator, done ); 137 /* => 138 1000 139 2500 140 3000 141 { 'true': [ [ 1, 2500 ], [ 0, 3000 ] ], 'false': [ [ 2, 1000 ] ] } 142 */ 143 ``` 144 145 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`. 146 147 ```javascript 148 function indicator( value, next ) { 149 setTimeout( onTimeout, value ); 150 function onTimeout() { 151 console.log( value ); 152 next( null, (value > 2000) ); 153 } 154 } 155 156 function done( error, result ) { 157 if ( error ) { 158 throw error; 159 } 160 console.log( result ); 161 } 162 163 var arr = [ 3000, 2500, 1000 ]; 164 165 var opts = { 166 'series': true 167 }; 168 169 groupByAsync( arr, opts, indicator, done ); 170 /* => 171 3000 172 2500 173 1000 174 { 'true': [ 3000, 2500 ], 'false': [ 1000 ] } 175 */ 176 ``` 177 178 To limit the maximum number of pending function invocations, set the `limit` option. 179 180 ```javascript 181 function indicator( value, next ) { 182 setTimeout( onTimeout, value ); 183 function onTimeout() { 184 console.log( value ); 185 next( null, (value > 2000) ); 186 } 187 } 188 189 function done( error, result ) { 190 if ( error ) { 191 throw error; 192 } 193 console.log( result ); 194 } 195 196 var arr = [ 3000, 2500, 1000 ]; 197 198 var opts = { 199 'limit': 2 200 }; 201 202 groupByAsync( arr, opts, indicator, done ); 203 /* => 204 2500 205 3000 206 1000 207 { 'true': [ 2500, 3000 ], 'false': [ 1000 ] } 208 */ 209 ``` 210 211 To set the execution context of the `indicator` function, set the `thisArg` option. 212 213 ```javascript 214 function indicator( value, next ) { 215 this.count += 1; 216 setTimeout( onTimeout, value ); 217 function onTimeout() { 218 next( null, (value > 2000) ); 219 } 220 } 221 222 var arr = [ 3000, 2500, 1000 ]; 223 224 var context = { 225 'count': 0 226 }; 227 228 var opts = { 229 'thisArg': context 230 }; 231 232 groupByAsync( arr, opts, indicator, done ); 233 234 function done( error, result ) { 235 if ( error ) { 236 throw error; 237 } 238 console.log( result ); 239 // => { 'true': [ 2500, 3000 ], 'false': [ 1000 ] } 240 241 console.log( context.count ); 242 // => 3 243 } 244 ``` 245 246 When invoked, the `indicator` function is provided a maximum of four arguments: 247 248 - `value`: collection value. 249 - `index`: collection index. 250 - `collection`: the input `collection`. 251 - `next`: a callback which should be called once the `indicator` function has finished processing a collection `value`. 252 253 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. 254 255 ```javascript 256 function indicator( value, i, collection, next ) { 257 console.log( 'collection: %s. %d: %d', collection.join( ',' ), i, value ); 258 setTimeout( onTimeout, value ); 259 function onTimeout() { 260 console.log( value ); 261 next( null, (value > 2000) ); 262 } 263 } 264 265 function done( error, result ) { 266 if ( error ) { 267 throw error; 268 } 269 console.log( result ); 270 } 271 272 var arr = [ 3000, 2500, 1000 ]; 273 274 groupByAsync( arr, indicator, done ); 275 /* => 276 collection: 3000,2500,1000. 0: 3000 277 collection: 3000,2500,1000. 1: 2500 278 collection: 3000,2500,1000. 2: 1000 279 1000 280 2500 281 3000 282 { 'true': [ 2500, 3000 ], 'false': [ 1000 ] } 283 */ 284 ``` 285 286 #### groupByAsync.factory( \[options,] indicator ) 287 288 Returns a `function` which invokes an `indicator` function once for each element in a `collection`. 289 290 ```javascript 291 function indicator( value, next ) { 292 setTimeout( onTimeout, value ); 293 function onTimeout() { 294 console.log( value ); 295 next( null, (value > 2000) ); 296 } 297 } 298 299 function done( error, result ) { 300 if ( error ) { 301 throw error; 302 } 303 console.log( result ); 304 } 305 306 var f = groupByAsync.factory( indicator ); 307 308 var arr1 = [ 3000, 2500, 1000 ]; 309 310 f( arr1, done ); 311 /* => 312 1000 313 2500 314 3000 315 { 'true': [ 2500, 3000 ], 'false': [ 1000 ] } 316 */ 317 318 var arr2 = [ 300, 250, 100 ]; 319 320 f( arr2, done ); 321 /* => 322 100 323 250 324 300 325 { 'false': [ 100, 250, 300 ] } 326 */ 327 ``` 328 329 The function accepts the same `options` as `groupByAsync()`. 330 331 </section> 332 333 <!-- /.usage --> 334 335 <!-- Package usage notes. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 336 337 <section class="notes"> 338 339 ## Notes 340 341 - 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`). 342 343 - 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. 344 345 - The function does **not** support dynamic `collection` resizing. 346 347 - The function does **not** skip `undefined` elements. 348 349 - If provided an empty `collection`, the function calls the `done` callback with an empty `object` for the group results. 350 351 - **Neither** `groupByAsync` 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`). 352 353 - The `group` returned by an `indicator` function should be a value which can be serialized as an `object` key. As a counterexample, 354 355 ```javascript 356 function indicator( value, next ) { 357 setTimeout( onTimeout, value ); 358 function onTimeout() { 359 console.log( value ); 360 next( null, {} ); 361 } 362 } 363 364 function done( error, result ) { 365 if ( error ) { 366 throw error; 367 } 368 console.log( result ); 369 } 370 371 var arr = [ 3000, 2500, 1000 ]; 372 373 groupByAsync( arr, indicator, done ); 374 /* => 375 1000 376 2500 377 3000 378 { '[object Object]': [ 1000, 2500, 3000 ] } 379 */ 380 ``` 381 382 while each group identifier is unique, all collection elements resolve to the same group because each group identifier serializes to the same `string`. 383 384 </section> 385 386 <!-- /.notes --> 387 388 <!-- Package usage examples. --> 389 390 <section class="examples"> 391 392 ## Examples 393 394 <!-- eslint no-undef: "error" --> 395 396 ```javascript 397 var resolve = require( 'path' ).resolve; 398 var readFile = require( '@stdlib/fs/read-file' ); 399 var groupByAsync = require( '@stdlib/utils/async/group-by' ); 400 401 var files = [ 402 resolve( __dirname, 'package.json' ), 403 resolve( __dirname, 'README.md' ), 404 resolve( __dirname, 'beep.boop.md' ) 405 ]; 406 407 function done( error, result ) { 408 if ( error ) { 409 throw error; 410 } 411 console.log( result ); 412 } 413 414 function indicator( file, next ) { 415 var opts = { 416 'encoding': 'utf8' 417 }; 418 readFile( file, opts, onFile ); 419 420 function onFile( error ) { 421 if ( error ) { 422 return next( null, 'nonreadable' ); 423 } 424 next( null, 'readable' ); 425 } 426 } 427 428 groupByAsync( files, indicator, done ); 429 ``` 430 431 </section> 432 433 <!-- /.examples --> 434 435 <!-- 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. --> 436 437 <section class="references"> 438 439 </section> 440 441 <!-- /.references --> 442 443 <!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 444 445 <section class="links"> 446 447 [mdn-array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array 448 449 [mdn-typed-array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray 450 451 [mdn-object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object 452 453 </section> 454 455 <!-- /.links -->