web-dev-qa-db-ja.com

JavaScript(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 } } }
16
Tejas Manohar

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';
});
32
georg

Georgeの答えを拡張して、ここに私が使用しているものがあります。この拡張された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);
      }
    },
  });
5
Chris Jones

編集:これは、キーではなくオブジェクトの値をマップします。私は質問を誤解しました。


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/

1
ste2425

Chris Jones's answer から少し改善しました。

次のコードは、配列要素の意図しない結果を修正します。

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

パフォーマンス効率としては最適ではありませんが、再帰部分をスキップしてコードをクリーンでシンプルに保ちたい場合は、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>
0