web-dev-qa-db-fra.com

Comment mapper en profondeur les clés d'objet avec JavaScript (lodash)?

https://lodash.com/docs#mapKeys

Est-il possible de cartographier les clés d'un objet à l'aide de Lodash? Si non, y a-t-il une autre bibliothèque fournissant cette fonctionnalité (si elle est groupée avec d'autres fonctionnalités d'itération et de manipulation profondes, c'est encore mieux!)? Sinon, comment procéder pour mettre cela en œuvre? La principale difficulté que je vois consiste à identifier des objets clé/valeur purs, en toute sécurité et profondément itérables. Il est facile de jeter des tableaux, mais il est important de noter que la fonction ne doit pas essayer de répéter en profondeur sur d'autres objets, comme une expression régulière, par exemple.

Résultat prévu

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 } } }
13
Tejas Manohar

Voici comment vous pouvez le faire dans 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;
    }
});

et voici un mixin plus abstrait, qui applique récursivement tout mappeur donné:

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

Utilisation (retourne comme ci-dessus):

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

Une autre option, avec une syntaxe plus élégante:

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

En prolongement de la réponse de georg, voici ce que j'utilise . Ce mixin étendu ajoute la possibilité de mapper des tableaux d'objets dans l'objet, un changement simple mais important.

_.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
Chris Jones

EDIT: Cela mappera les valeurs des objets, pas ses clés. J'ai mal compris la 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;
}

Et l'utiliser comme

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

Il effectuera une itération récursive sur les propriétés énumérables propres aux objets et appellera un CB en lui transmettant la valeur et la clé actuelles. Les valeurs renvoyées remplaceront la valeur existante pour le nouvel objet. Il ne mute pas l'objet d'origine.

Voir le violon: https://jsfiddle.net/1veppve1/

1
ste2425

J'ai ajouté une petite amélioration par rapport à la réponse de Chris Jones .

La suite du code corrige certains résultats inattendus des éléments 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);
    }
  },
});
1
Sunho Hong

Pas le meilleur en termes d’efficacité, mais si vous voulez ignorer une partie de la récursivité et garder le code propre et simple, vous pouvez le stringifier en json et utiliser JSON.parse avec un paramètre supplémentaire (rappel) et le transformer les clés avec lodash.

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

Voici un exemple de travail:

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
Koushik Chatterjee