How to deeply map object keys with JavaScript (lodash)?

*爱你&永不变心* 提交于 2019-12-03 07:07:55

问题


https://lodash.com/docs#mapKeys

Is it possible to map an Object's keys deeply using Lodash? If not, is there another library providing this functionality (if grouped with other deep iteration and manipulation functionality, even better!)? Else, how would one go about implementing this? The main struggle I see is in identifying pure key/value objects that are safely, deeply iterable. It's easy to throw out Arrays, but it's important to note that the function shouldn't try to deeply iterate on other objects like a regex, for example.

Intended result-

var obj = { a: 2, b: { c: 2, d: { a: 3 } } };
_.deepMapKeys(obj, function (val, key) {
  return key + '_hi';
});
// => { a_hi: 2, b_hi: { c_hi: 2, d_hi: { a_hi: 3 } } }

回答1:


Here's how you can do that in lodash:

_.mixin({
    'deepMapKeys': function (obj, fn) {

        var x = {};

        _.forOwn(obj, function(v, k) {
            if(_.isPlainObject(v))
                v = _.deepMapKeys(v, fn);
            x[fn(v, k)] = v;
        });

        return x;
    }
});

and here's a more abstract mixin, that recursively applies any given mapper:

_.mixin({
    deep: function (obj, mapper) {
        return mapper(_.mapValues(obj, function (v) {
            return _.isPlainObject(v) ? _.deep(v, mapper) : v;
        }));
    },
});

Usage (returns the same as above):

obj = _.deep(obj, function(x) {
    return _.mapKeys(x, function (val, key) {
        return key + '_hi';
    });
});

Another option, with more elegant syntax:

_.mixin({
    deeply: function (map) {
        return function(obj, fn) {
            return map(_.mapValues(obj, function (v) {
                return _.isPlainObject(v) ? _.deeply(map)(v, fn) : v;
            }), fn);
        }
    },
});


obj = _.deeply(_.mapKeys)(obj, function (val, key) {
    return key + '_hi';
});



回答2:


In extention of georg's answer, here's what I'm using. This extended mixin adds the ability to map arrays of objects within the object too, a simple but important change.

_.mixin({
    deeply: function (map) {
      return function (obj, fn) {
        return map(_.mapValues(obj, function (v) {
          return _.isPlainObject(v) ? _.deeply(map)(v, fn) : _.isArray(v) ? v.map(function(x) {
            return _.deeply(map)(x, fn);
          }) : v;
        }), fn);
      }
    },
  });



回答3:


EDIT: This will map the objects values, not its keys. I misunderstood the question.


function deepMap (obj, cb) {
    var out = {};

    Object.keys(obj).forEach(function (k) {
      var val;

      if (obj[k] !== null && typeof obj[k] === 'object') {
        val = deepMap(obj[k], cb);
      } else {
        val = cb(obj[k], k);
      }

      out[k] = val;
    });

  return out;
}

And use it as

var lol = deepMap(test, function (v, k) {
    return v + '_hi';
});

It will recursively iterate over an objects own enumerable properties and call a CB passing it the current value and key. The values returned will replace the existing value for the new object. It doesn't mutate the original object.

See fiddle: https://jsfiddle.net/1veppve1/




回答4:


I've add a small improvement from Chris Jones's answer.

Following code fix some unintended results of Array elements.

_.mixin({
  deeply: function (map) {
    var deeplyArray = function (obj, fn) {
      return obj.map(function(x) {
        return _.isPlainObject(x) ? _.deeply(map)(x, fn) : x;
      })
    }

    return function (obj, fn) {
      if (_.isArray(obj)) {
        return deeplyArray(obj, fn);
      }

      return map(_.mapValues(obj, function (v) {
        return _.isPlainObject(v) ? _.deeply(map)(v, fn) : _.isArray(v) ? 
          deeplyArray(v, fn) : v;
      }), fn);
    }
  },
});



回答5:


Not a best one as per performance efficiency, but If you want to skip recursion part from your side and want to keep the code clean and simple then you can stringify it to json and use JSON.parse with additional parameter (callback) and there you can transform the keys with lodash.

JSON.parse(JSON.stringify(obj), (k, v) => _.isObject(v) ? _.mapKeys(v, (_v, _k) => _k + '_hi') : v)

Here is an working example:

let obj = { a: 2, b: { c: 2, d: { a: 3 } } };
let mappedObj = JSON.parse(JSON.stringify(obj), (k, v) => _.isObject(v) ? _.mapKeys(v, (_v, _k) => _k + '_hi') : v)
console.log(mappedObj)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>


来源:https://stackoverflow.com/questions/35055731/how-to-deeply-map-object-keys-with-javascript-lodash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!