Skip to content

Instantly share code, notes, and snippets.

@rixtox
Last active August 29, 2015 13:57
Show Gist options
  • Save rixtox/9501343 to your computer and use it in GitHub Desktop.
Save rixtox/9501343 to your computer and use it in GitHub Desktop.
JavaScript Asynchronous Deep Mapping With Promise

deepMap(object, iterator [, routePrefix])

Iterate and map recursively into an object. Return a promise object with a standard then interface created by Q. The depth and data types can be controlled by the iterator.

When encounters an array object, the returned value is an object use the indices of the array as its keys in String.

Note, that module q is required to handle the async quenue and create promises.

Arguments

  • object - An array to iterate over.
  • iterator(item, route, parent) - A function to apply to each item in the array. Should return an array of results or a thenable promise.
    • item - A value of the current item.
    • route - An array represents the route of the current item in the object. route[0] is the current item's name, and last one the the root's name.
    • parent - An object contains the current item if the current item is iterated from a parent object. Otherwise it's undefined.
    • return value - A valid promise which when success, an array [newItem, newKey, stepIn] is resolved, where newItem is the transformed item to replace the original item; newKey is the new key name of the current item, which is often a String; and stepIn is a Boolean value as a switch of whether to iterate into the current item or not. Otherwise the resolved array can be directly returned without a wrapper of fulfilled promise.
  • routePrefix - An array acts like the route argument passed to the iterator, but is a root of all route created and passed to the iterator.

Helper

  • deepMap.typefor(object) - Call on Object.prototype.toString.call() and get the type name in lower case. For example, deepMap.typefor([]) returns "array".
Q = require 'q'
deepMap = (obj, iterator, routePrefix = []) ->
switch typefor obj
when 'object' then keys = Object.keys obj
when 'array' then keys = [0...obj.length]
else obj = [obj]; keys = [0]; single = yes
results = {}
promises = keys.map (key) ->
deferred = Q.defer()
resolved = (newItem, newKey, stepIn) ->
add = (newItem) ->
deferred.resolve results[newKey] = newItem
unless stepIn
return add newItem
_route = [].concat newKey, route[1..]
deepMap newItem, iterator, _route
.then add
item = obj[key]
route = [].concat key, routePrefix
Q iterator item, route, obj
.spread resolved
return deferred.promise
Q promises
.all().then ->
if single
return v for k, v of results
else results
typefor = (o) ->
Object.prototype.toString.call(o)
.match(/\[object (.*)\]/)[1]
.toLowerCase()
deepMap.typefor = typefor
module.exports = deepMap
Q = require 'q'
deepMap = require './deepMap'
{typefor} = deepMap
obj =
a: 'a'
b: ['0b'
'1b'
a2b: 'a2b'
b2b: -> 'b2b']
c: 'c'
d:
ad:'ad'
bd:'bd'
cd: ['1cd'
'2cd']
iterator = (item, route, parent) ->
newKey = route[0] + 'ok'
switch typefor item
when 'function'
[item(), newKey, true ]
when 'string'
deferred = Q.defer()
setTimeout ->
deferred.resolve ["resolved #{item}", newKey]
deferred.promise
else
[item, newKey, true ]
deepMap obj, iterator
.then console.log
### Results:
{ aok: 'resolved a',
cok: 'resolved c',
dok:
{ adok: 'resolved ad',
bdok: 'resolved bd',
cdok: { '0ok': 'resolved 1cd', '1ok': 'resolved 2cd' } },
bok:
{ '0ok': 'resolved 0b',
'1ok': 'resolved 1b',
'2ok': { a2bok: 'resolved a2b', b2bok: 'resolved b2b' } } }
###
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment