README.md (9268B)
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 # mapKeysAsync 22 23 > Map keys from one object to a new object having the same values. 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 mapKeysAsync = require( '@stdlib/utils/async/map-keys' ); 41 ``` 42 43 #### mapKeysAsync( obj, \[options,] transform, done ) 44 45 Map keys from one `object` to a new `object` having the same values. 46 47 ```javascript 48 function transform( key, next ) { 49 setTimeout( onTimeout, 0 ); 50 function onTimeout() { 51 next( null, key+':beep' ); 52 } 53 } 54 55 function done( error, out ) { 56 if ( error ) { 57 throw error; 58 } 59 console.log( out ); 60 // => { 'a:beep': 1, 'b:beep': 2 } 61 } 62 63 var obj = { 64 'a': 1, 65 'b': 2 66 }; 67 68 mapKeysAsync( obj, transform, done ); 69 ``` 70 71 The `next` callback accepts two arguments: `error` and `key`. The second argument to the `next` callback is the transformed property name. If a `transform` function calls the `next` callback with a truthy error argument, the function stops processing any additional own properties and calls the `done` callback for error processing. 72 73 ```javascript 74 function transform( key, value, next ) { 75 setTimeout( onTimeout, value ); 76 function onTimeout() { 77 if ( value === 1 ) { 78 return next( new Error( 'boop' ) ); 79 } 80 next( null, key+':beep' ); 81 } 82 } 83 84 function done( error ) { 85 if ( error ) { 86 console.error( error.message ); 87 // => 'boop' 88 } 89 } 90 91 var obj = { 92 'a': 1, 93 'b': 2 94 }; 95 96 mapKeysAsync( obj, transform, done ); 97 ``` 98 99 The function accepts the following `options`: 100 101 - `limit`: the maximum number of pending invocations at any one time. Default: `infinity`. 102 - `series`: `boolean` indicating whether to sequentially invoke the `transform` function for each own property. If `true`, the function sets `options.limit=1`. Default: `false`. 103 - `thisArg`: the execution context for `fcn`. 104 105 By default, all properties are processed concurrently, which means that the function does **not** guarantee completion order. To process each property sequentially, set the `series` option to `true`. 106 107 ```javascript 108 function transform( key, value, next ) { 109 setTimeout( onTimeout, value ); 110 function onTimeout() { 111 next( null, key+':beep' ); 112 } 113 } 114 115 function done( error, out ) { 116 if ( error ) { 117 throw error; 118 } 119 console.log( out ); 120 // => { 'a:beep': 1, 'b:beep': 2 } 121 } 122 123 var obj = { 124 'a': 1, 125 'b': 2 126 }; 127 128 var opts = { 129 'series': true 130 }; 131 132 mapKeysAsync( obj, opts, transform, done ); 133 ``` 134 135 To limit the maximum number of pending function invocations, set the `limit` option. 136 137 ```javascript 138 function transform( key, value, next ) { 139 setTimeout( onTimeout, value ); 140 function onTimeout() { 141 next( null, key+':beep' ); 142 } 143 } 144 145 function done( error, out ) { 146 if ( error ) { 147 throw error; 148 } 149 console.log( out ); 150 // => { 'a:beep': 1, 'b:beep': 2, 'c:beep': 3 } 151 } 152 153 var obj = { 154 'a': 1, 155 'b': 2, 156 'c': 3 157 }; 158 159 var opts = { 160 'limit': 2 161 }; 162 163 mapKeysAsync( obj, opts, transform, done ); 164 ``` 165 166 To set the execution context of the `transform` function, set the `thisArg` option. 167 168 ```javascript 169 function transform( key, value, next ) { 170 this.count += 1; 171 setTimeout( onTimeout, value ); 172 function onTimeout() { 173 next( null, key+':beep' ); 174 } 175 } 176 177 var obj = { 178 'a': 1, 179 'b': 2, 180 'c': 3 181 }; 182 183 var context = { 184 'count': 0 185 }; 186 187 var opts = { 188 'thisArg': context 189 }; 190 191 mapKeysAsync( obj, opts, transform, done ); 192 193 function done( error, out ) { 194 if ( error ) { 195 throw error; 196 } 197 console.log( out ); 198 // => { 'a:beep': 1, 'b:beep': 2, 'c:beep': 3 } 199 200 console.log( context.count ); 201 // => 3 202 } 203 ``` 204 205 When invoked, the `transform` function is provided a maximum of four arguments: 206 207 - `key`: object key. 208 - `value`: object value corresponding to `key`. 209 - `obj`: source object. 210 - `next`: a callback which should be called once the `transform` function has finished processing a property. 211 212 The actual number of provided arguments depends on function `length`. If the `transform` function accepts two arguments, the `transform` function is provided `key` and `next`. If the `transform` function accepts three arguments, the `transform` function is provided `key`, `value`, and `next`. For every other `transform` function signature, the `transform` function is provided all four arguments. 213 214 ```javascript 215 function transform( key, value, obj, next ) { 216 console.log( 'obj: %s. %s: %d', JSON.stringify( obj ), key, value ); 217 setTimeout( onTimeout, value ); 218 function onTimeout() { 219 next( null, key+':'+value ); 220 } 221 } 222 223 function done( error, out ) { 224 if ( error ) { 225 throw error; 226 } 227 console.log( out ); 228 } 229 230 var obj = { 231 'a': 1, 232 'b': 2 233 }; 234 235 mapKeysAsync( obj, transform, done ); 236 /* => e.g., 237 obj: {"a": 1, "b": 2}. a: 1 238 obj: {"a": 1, "b": 2}. b: 2 239 { 'a:1': 1, 'b:2': 2 } 240 */ 241 ``` 242 243 #### mapKeysAsync.factory( \[options,] transform ) 244 245 Returns a `function` which invokes a `transform` function once for each own property. 246 247 ```javascript 248 function transform( key, value, next ) { 249 setTimeout( onTimeout, value ); 250 function onTimeout() { 251 next( null, key+':beep' ); 252 } 253 } 254 255 function done( error, out ) { 256 if ( error ) { 257 throw error; 258 } 259 console.log( out ); 260 } 261 262 var f = mapKeysAsync.factory( transform ); 263 264 var obj1 = { 265 'a': 1, 266 'b': 2 267 }; 268 269 f( obj1, done ); 270 // => { 'a:beep': 1, 'b:beep': 2 } 271 272 var obj2 = { 273 'c': 3, 274 'd': 4 275 }; 276 277 f( obj2, done ); 278 // => { 'c:beep': 3, 'd:beep': 4 } 279 ``` 280 281 The function accepts the same `options` as `mapKeysAsync()`. 282 283 </section> 284 285 <!-- /.usage --> 286 287 <!-- Package usage notes. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 288 289 <section class="notes"> 290 291 ## Notes 292 293 - 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. 294 - If provided an empty `object`, the function calls the `done` callback with an empty `object`. 295 - Key iteration order is **not** guaranteed, as `object` key enumeration is not specified according to the [ECMAScript specification][ecma-262]. In practice, however, most engines use insertion order to sort an `object`'s keys, thus allowing for iteration order. 296 - Key insertion order is **not** guaranteed. 297 - The value returned by a `transform` function should be a value which can be serialized as an `object` key. 298 - The function only maps **own** properties. Hence, the function does **not** map inherited properties. 299 - The function **shallow** copies key values. 300 - **Neither** `mapKeysAsync` 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`). 301 302 </section> 303 304 <!-- /.notes --> 305 306 <!-- Package usage examples. --> 307 308 <section class="examples"> 309 310 ## Examples 311 312 <!-- eslint no-undef: "error" --> 313 314 ```javascript 315 var resolve = require( 'path' ).resolve; 316 var readFile = require( '@stdlib/fs/read-file' ); 317 var mapKeysAsync = require( '@stdlib/utils/async/map-keys' ); 318 319 var files = { 320 'file1': resolve( __dirname, 'package.json' ), 321 'file2': resolve( __dirname, 'README.md' ) 322 }; 323 324 function read( key, value, next ) { 325 var opts = { 326 'encoding': 'utf8' 327 }; 328 readFile( value, opts, onFile ); 329 330 function onFile( error ) { 331 if ( error ) { 332 return next( error, key+':unreadable' ); 333 } 334 next( null, key+':readable' ); 335 } 336 } 337 338 function done( error, out ) { 339 if ( error ) { 340 throw error; 341 } 342 console.log( out ); 343 } 344 345 mapKeysAsync( files, read, done ); 346 ``` 347 348 </section> 349 350 <!-- /.examples --> 351 352 <!-- 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. --> 353 354 <section class="references"> 355 356 </section> 357 358 <!-- /.references --> 359 360 <!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 361 362 <section class="links"> 363 364 [ecma-262]: http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4 365 366 </section> 367 368 <!-- /.links -->