Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS - deep map function

Underscore.js has a very useful map function.

_.map([1, 2, 3], function(num){ return num * 3; });
=> [3, 6, 9]
_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]

I am looking for a similar function that can iterate through nested objects, or deep mapping. After a ton of searching I can't really find this. What I can find is something to pluck a deep object, but not iterate through every value of a deep object.

Something like this:

deepMap({
  one: 1,
  two: [
    { foo: 'bar' },
    { foos: ['b', 'a', 'r', 's'] },
  ],
  three: [1, 2, 3]
}, function(val, key) {
  return (String(val).indexOf('b') > -1) ? 'bobcat' : val;
})

How would one do this?

Sample Output

{
  one: 1,
  two: [
    { foo: 'bobcat' },
    { foos: ['bobcat', 'a', 'r', 's'] },
  ],
  three: [1, 2, 3]
}
like image 559
dthree Avatar asked Aug 15 '14 20:08

dthree


2 Answers

Here's a Lodash solution using transform

function deepMap(obj, iterator, context) {
    return _.transform(obj, function(result, val, key) {
        result[key] = _.isObject(val) /*&& !_.isDate(val)*/ ?
                            deepMap(val, iterator, context) :
                            iterator.call(context, val, key, obj);
    });
}

_.mixin({
   deepMap: deepMap
});
like image 160
megawac Avatar answered Sep 29 '22 13:09

megawac


Here is a clean ES6 version:

function mapObject(obj, fn) {
  return Object.keys(obj).reduce(
    (res, key) => {
      res[key] = fn(obj[key]);
      return res;
    },
    {}
  )
}

function deepMap(obj, fn) {
  const deepMapper = val => typeof val === 'object' ? deepMap(val, fn) : fn(val);
  if (Array.isArray(obj)) {
    return obj.map(deepMapper);
  }
  if (typeof obj === 'object') {
    return mapObject(obj, deepMapper);
  }
  return obj;
}
like image 23
Geoffroy Warin Avatar answered Sep 29 '22 13:09

Geoffroy Warin