如何使用JavaScript(lodash)深度映射对象键?

时间:2016-01-28 07:57:04

标签: javascript object key underscore.js lodash

https://lodash.com/docs#mapKeys

是否可以使用Lodash深度映射对象的键?如果没有,是否有另一个库提供此功能(如果与其他深度迭代和操作功能组合,甚至更好!)?另外,如何实现这一目标呢?我看到的主要斗争是识别安全,深度可迭代的纯键/值对象。抛出数组很容易,但重要的是要注意函数不应该尝试深度迭代其他对象,例如正则表达式。

预期结果 -

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 } } }

5 个答案:

答案 0 :(得分:30)

以下是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;
    }
});

这里是一个更抽象的mixin,递归地应用任何给定的映射器:

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

用法(返回与上面相同):

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

另一种选择,语法更优雅:

_.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';
});

答案 1 :(得分:3)

在延伸georg的答案中,这就是我正在使用的内容。 这个扩展的mixin也增加了在对象内映射对象数组的能力,这是一个简单但重要的变化。

_.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);
      }
    },
  });

答案 2 :(得分:1)

编辑:这将映射对象值,而不是其键。我误解了这个问题。

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;
}

并将其用作

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

它将递归迭代对象拥有的可枚举属性,并调用CB传递当前值和键。返回的值将替换新对象的现有值。它不会改变原始对象。

请参阅小提琴:https://jsfiddle.net/1veppve1/

答案 3 :(得分:1)

我从Chris Jones's answer添加了一些小改进。

以下代码修复了Array元素的一些意外结果。

_.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);
    }
  },
});

答案 4 :(得分:0)

就性能而言,这不是最好的选择,但是如果您想从自己的角度跳过递归部分,并希望保持代码简洁明了,则可以将其字符串化为json并使用带有附加参数的JSON.parse(回调),然后您可以使用lodash变换键。

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

这是一个有效的示例:

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>