# mapKeysAsync > Map keys from one object to a new object having the same values.
## Usage ```javascript var mapKeysAsync = require( '@stdlib/utils/async/map-keys' ); ``` #### mapKeysAsync( obj, \[options,] transform, done ) Map keys from one `object` to a new `object` having the same values. ```javascript function transform( key, next ) { setTimeout( onTimeout, 0 ); function onTimeout() { next( null, key+':beep' ); } } function done( error, out ) { if ( error ) { throw error; } console.log( out ); // => { 'a:beep': 1, 'b:beep': 2 } } var obj = { 'a': 1, 'b': 2 }; mapKeysAsync( obj, transform, done ); ``` 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. ```javascript function transform( key, value, next ) { setTimeout( onTimeout, value ); function onTimeout() { if ( value === 1 ) { return next( new Error( 'boop' ) ); } next( null, key+':beep' ); } } function done( error ) { if ( error ) { console.error( error.message ); // => 'boop' } } var obj = { 'a': 1, 'b': 2 }; mapKeysAsync( obj, transform, done ); ``` The function accepts the following `options`: - `limit`: the maximum number of pending invocations at any one time. Default: `infinity`. - `series`: `boolean` indicating whether to sequentially invoke the `transform` function for each own property. If `true`, the function sets `options.limit=1`. Default: `false`. - `thisArg`: the execution context for `fcn`. 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`. ```javascript function transform( key, value, next ) { setTimeout( onTimeout, value ); function onTimeout() { next( null, key+':beep' ); } } function done( error, out ) { if ( error ) { throw error; } console.log( out ); // => { 'a:beep': 1, 'b:beep': 2 } } var obj = { 'a': 1, 'b': 2 }; var opts = { 'series': true }; mapKeysAsync( obj, opts, transform, done ); ``` To limit the maximum number of pending function invocations, set the `limit` option. ```javascript function transform( key, value, next ) { setTimeout( onTimeout, value ); function onTimeout() { next( null, key+':beep' ); } } function done( error, out ) { if ( error ) { throw error; } console.log( out ); // => { 'a:beep': 1, 'b:beep': 2, 'c:beep': 3 } } var obj = { 'a': 1, 'b': 2, 'c': 3 }; var opts = { 'limit': 2 }; mapKeysAsync( obj, opts, transform, done ); ``` To set the execution context of the `transform` function, set the `thisArg` option. ```javascript function transform( key, value, next ) { this.count += 1; setTimeout( onTimeout, value ); function onTimeout() { next( null, key+':beep' ); } } var obj = { 'a': 1, 'b': 2, 'c': 3 }; var context = { 'count': 0 }; var opts = { 'thisArg': context }; mapKeysAsync( obj, opts, transform, done ); function done( error, out ) { if ( error ) { throw error; } console.log( out ); // => { 'a:beep': 1, 'b:beep': 2, 'c:beep': 3 } console.log( context.count ); // => 3 } ``` When invoked, the `transform` function is provided a maximum of four arguments: - `key`: object key. - `value`: object value corresponding to `key`. - `obj`: source object. - `next`: a callback which should be called once the `transform` function has finished processing a property. 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. ```javascript function transform( key, value, obj, next ) { console.log( 'obj: %s. %s: %d', JSON.stringify( obj ), key, value ); setTimeout( onTimeout, value ); function onTimeout() { next( null, key+':'+value ); } } function done( error, out ) { if ( error ) { throw error; } console.log( out ); } var obj = { 'a': 1, 'b': 2 }; mapKeysAsync( obj, transform, done ); /* => e.g., obj: {"a": 1, "b": 2}. a: 1 obj: {"a": 1, "b": 2}. b: 2 { 'a:1': 1, 'b:2': 2 } */ ``` #### mapKeysAsync.factory( \[options,] transform ) Returns a `function` which invokes a `transform` function once for each own property. ```javascript function transform( key, value, next ) { setTimeout( onTimeout, value ); function onTimeout() { next( null, key+':beep' ); } } function done( error, out ) { if ( error ) { throw error; } console.log( out ); } var f = mapKeysAsync.factory( transform ); var obj1 = { 'a': 1, 'b': 2 }; f( obj1, done ); // => { 'a:beep': 1, 'b:beep': 2 } var obj2 = { 'c': 3, 'd': 4 }; f( obj2, done ); // => { 'c:beep': 3, 'd:beep': 4 } ``` The function accepts the same `options` as `mapKeysAsync()`.
## Notes - 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. - If provided an empty `object`, the function calls the `done` callback with an empty `object`. - 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. - Key insertion order is **not** guaranteed. - The value returned by a `transform` function should be a value which can be serialized as an `object` key. - The function only maps **own** properties. Hence, the function does **not** map inherited properties. - The function **shallow** copies key values. - **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`).
## Examples ```javascript var resolve = require( 'path' ).resolve; var readFile = require( '@stdlib/fs/read-file' ); var mapKeysAsync = require( '@stdlib/utils/async/map-keys' ); var files = { 'file1': resolve( __dirname, 'package.json' ), 'file2': resolve( __dirname, 'README.md' ) }; function read( key, value, next ) { var opts = { 'encoding': 'utf8' }; readFile( value, opts, onFile ); function onFile( error ) { if ( error ) { return next( error, key+':unreadable' ); } next( null, key+':readable' ); } } function done( error, out ) { if ( error ) { throw error; } console.log( out ); } mapKeysAsync( files, read, done ); ```