Mapper sur les clés de préservation des objets

129

La mapfonction dans underscore.js, si elle est appelée avec un objet javascript, retourne un tableau de valeurs mappées à partir des valeurs de l'objet.

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

y a-t-il un moyen de lui faire conserver les clés? c'est à dire, je veux une fonction qui retourne

{one: 3, two: 6, three: 9}
xuanji
la source
Si, comme moi, vous êtes venu ici à la recherche d'une fonction de type `` mapValues ​​'' qui change l'objet réel au lieu d'en renvoyer un nouveau, consultez cette solution simple: stackoverflow.com/questions/30894044/…
Michael Trouw

Réponses:

228

Avec soulignement

Underscore fournit une fonction _.mapObjectpour mapper les valeurs et conserver les clés.

_.mapObject({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; });

// => { one: 3, two: 6, three: 9 }

DEMO


Avec Lodash

Lodash fournit une fonction _.mapValuespour mapper les valeurs et conserver les clés.

_.mapValues({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; });

// => { one: 3, two: 6, three: 9 }

DEMO

GG.
la source
Des efforts ont été faits pour obtenir une fonction _.mapValues ​​en trait de soulignement: problème pertinent , demande d'extraction pour _.mapValues . J'espère que cela
passera
il me semble que vous transformez un OBJET en objet, ou suis-je fou?
jsh
@jsh Dans ce cas, _.map()renvoie [['one', 3], ['two', 6], ['three', 9]], qui est un tableau de tableaux, et le _.object()transforme en objet.
Jezen Thomas
56

J'ai réussi à trouver la fonction requise dans lodash, une bibliothèque utilitaire similaire au soulignement.

http://lodash.com/docs#mapValues

_.mapValues(object, [callback=identity], [thisArg])

Crée un objet avec les mêmes clés que l'objet et les valeurs générées en exécutant chaque propre propriété énumérable d'objet via le rappel. Le rappel est lié à thisArg et appelé avec trois arguments; (valeur, clé, objet).

xuanji
la source
19

var mapped = _.reduce({ one: 1, two: 2, three: 3 }, function(obj, val, key) {
    obj[key] = val*3;
    return obj;
}, {});

console.log(mapped);
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>

kunalgolani
la source
13

Je sais que c'est vieux, mais maintenant Underscore a une nouvelle carte pour les objets:

_.mapObject(object, iteratee, [context]) 

Vous pouvez bien sûr créer une carte flexible pour les tableaux et les objets

_.fmap = function(arrayOrObject, fn, context){
    if(this.isArray(arrayOrObject))
      return _.map(arrayOrObject, fn, context);
    else
      return _.mapObject(arrayOrObject, fn, context);
}
Rayjax
la source
5
Note aux utilisateurs de lodash: jdalton a décidé de rompre la compatibilité avec underscore.js suite à ce changement. lodash ne soutiendra pas mapObject; regardez plutôt la mapValuesméthode de lodash .
Mark Amery
13

Que diriez-vous de cette version en JS ordinaire ( ES6 / ES2015 )?

let newObj = Object.assign(...Object.keys(obj).map(k => ({[k]: obj[k] * 3})));

jsbin

Si vous voulez mapper sur un objet de manière récursive (mapper obj imbriqué), cela peut être fait comme ceci:

const mapObjRecursive = (obj) => {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === 'object') obj[key] = mapObjRecursive(obj[key]);
    else obj[key] = obj[key] * 3;
  });
  return obj;
};

jsbin

Depuis ES7 / ES2016, vous pouvez utiliser Object.entriesau lieu de Object.keysceci:

let newObj = Object.assign(...Object.entries(obj).map([k, v] => ({[k]: v * 3})));
Rotareti
la source
5

Je sais que cela fait longtemps, mais la solution la plus évidente via le repli (aka réduire en js) est manquante, par souci d'exhaustivité, je la laisserai ici:

function mapO(f, o) {
  return Object.keys(o).reduce((acc, key) => {
    acc[key] = f(o[key])
    return acc
  }, {})
}
Darwin
la source
Je suis d'accord avec l'utilisation de la bibliothèque Lodash mais les programmeurs utilisent ce type de bibliothèques et ne savent pas comment cela pourrait être réalisé dans vanilla JS. Alors, j'apprécie votre réponse!
cyonder
3

_.map renvoie un tableau, pas un objet.

Si vous voulez un objet, il vaut mieux utiliser une fonction différente, comme each; si vous voulez vraiment utiliser map, vous pouvez faire quelque chose comme ceci:

Object.keys(object).map(function(value, index) {
   object[value] *= 3;
})

mais c'est déroutant, quand mapon voit, on s'attendrait à avoir un tableau comme résultat et à en faire quelque chose.

Alberto Zaccagni
la source
J'ai eu le sentiment en lisant la documentation qu'il ne serait pas naturel d'essayer cela dans underscore.js. Je pense que mon cas d'utilisation est assez naturel, pourquoi ne le supportent-ils pas?
xuanji le
Une raison pourrait probablement être qu'elle mapest utilisée pour modifier une entrée produisant un tableau en sortie, vous pouvez composer _.objectet en _.maptant que @GG. écrit, mais c'est une question de goût à ce stade.
Alberto Zaccagni
3

Je pense que vous voulez une fonction mapValues (pour mapper une fonction sur les valeurs d'un objet), qui est assez facile à implémenter vous-même:

mapValues = function(obj, f) {
  var k, result, v;
  result = {};
  for (k in obj) {
    v = obj[k];
    result[k] = f(v);
  }
  return result;
};
joyrexus
la source
2
const mapObject = (obj = {}, mapper) =>
  Object.entries(obj).reduce(
    (acc, [key, val]) => ({ ...acc, [key]: mapper(val) }),
    {},
  );
Nigel Kirby
la source
J'étais sur le point d'ajouter moi-même cette réponse. Heureux d'avoir fait défiler!
Bill Criswell le
0

Un correctif de mix pour le bogue de la carte de soulignement : P

_.mixin({ 
    mapobj : function( obj, iteratee, context ) {
        if (obj == null) return [];
        iteratee = _.iteratee(iteratee, context);
        var keys = obj.length !== +obj.length && _.keys(obj),
            length = (keys || obj).length,
            results = {},
            currentKey;
        for (var index = 0; index < length; index++) {
          currentKey = keys ? keys[index] : index;
          results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
        }
        if ( _.isObject( obj ) ) {
            return _.object( results ) ;
        } 
        return results;
    }
}); 

Une solution de contournement simple qui garde la bonne clé et la renvoie en tant qu'objet Elle est toujours utilisée de la même manière qu'en invité, vous pouvez utiliser cette fonction pour remplacer la fonction bugy _.map

ou tout simplement comme je l'ai utilisé comme mixin

_.mapobj ( options , function( val, key, list ) 
Pascal
la source