Question fonction de carte pour les objets (au lieu de tableaux)


J'ai un objet

myObject = { 'a': 1, 'b': 2, 'c': 3 }

Je cherche une méthode native, similaire à Array.prototype.map cela serait utilisé comme suit:

newObject = myObject.map(function (value, label) {
    return value * value;
});

// newObject is now { 'a': 1, 'b': 4, 'c': 9 }

Est-ce que JavaScript a un tel map fonction pour les objets? (Je le veux pour Node.JS, donc je ne me soucie pas des problèmes entre navigateurs.)


574
2018-02-11 10:41


origine


Réponses:


Il n'y a pas de natif map au Object objet, mais qu'en est-il de ceci:

Object.keys(myObject).map(function(key, index) {
   myObject[key] *= 2;
});

console.log(myObject);

// => { 'a': 2, 'b': 4, 'c': 6 }

Mais vous pouvez facilement itérer sur un objet en utilisant for ... in:

for(var key in myObject) {
    if(myObject.hasOwnProperty(key)) {
        myObject[key] *= 2;
    }
}

Mettre à jour

Beaucoup de gens mentionnent que les méthodes précédentes ne renvoient pas un nouvel objet, mais opèrent plutôt sur l'objet lui-même. D'ailleurs je voulais ajouter une autre solution qui retourne un nouvel objet et laisse l'objet original tel qu'il est:

var newObject = Object.keys(myObject).reduce(function(previous, current) {
    previous[current] = myObject[current] * myObject[current];
    return previous;
}, {});

console.log(newObject);
// => { 'a': 1, 'b': 4, 'c': 9 }

console.log(myObject);
// => { 'a': 1, 'b': 2, 'c': 3 }

Array.prototype.reduce réduit un tableau à une seule valeur en fusionnant quelque peu la valeur précédente avec le courant. La chaîne est initialisée par un objet vide {}. À chaque itération, une nouvelle clé de myObject est ajouté avec son carré en tant que valeur.


830
2018-02-11 10:52



Que diriez-vous d'un one liner avec assignation variable immédiate en JS simple (ES6 / ES2015)?

Faire usage de opérateur de propagation et nom de clé calculé syntaxe:

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

jsbin

Une autre version utilisant réduire:

let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {});

jsbin

Premier exemple en fonction:

const oMap = (o, f) => Object.assign({}, ...Object.keys(o).map(k => ({ [k]: f(o[k]) })));

// To square each value you can call it like this:
let mappedObj = oMap(myObj, (x) => x * x);

jsbin

Si vous voulez mapper un objet imbriqué récursivement dans un fonctionnel style, il peut être fait comme ceci:

const sqrObjRecursive = (obj) => 
  Object.keys(obj).reduce((newObj, key) => 
    (obj[key] && typeof obj[key] === 'object') ?
      {...newObj, [key]: sqrObjRecursive(obj[key])} :  // recurse.
      {...newObj, [key]: obj[key] * obj[key]}          // square val.
    ,{})       

jsbin

Ou plus impérativement, comme ceci:

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

jsbin

Depuis ES7 / ES2016 vous pouvez utiliser Object.entries au lieu de Object.keys par exemple. comme ça:

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


Propriétés héritées et chaîne de prototypes:

Dans certaines situations rares, vous devrez peut-être cartographier comme en classe objet qui contient les propriétés d'un objet hérité sur son chaîne prototype. Dans ces cas Object.keys() ne fonctionnera pas, parce que Object.keys() n'énumère pas hérité Propriétés. Si vous avez besoin de cartographier hérité propriétés, vous devez utiliser for (key in myObj) {...}.

Voici un exemple d'objet qui hérite des propriétés d'un autre objet et comment Object.keys() ne fonctionne pas dans un tel scénario.

const obj1 = { 'a': 1, 'b': 2, 'c': 3}
const obj2 = Object.create(obj1);  // One of multiple ways to inherit an object in JS.

// Here you see how the properties of obj1 sit on the 'prototype' of obj2
console.log(obj2)  // Prints: obj2.__proto__ = { 'a': 1, 'b': 2, 'c': 3}

console.log(Object.keys(obj2));  // Prints: an empty Array.

for (key in obj2) {
  console.log(key);              // Prints: 'a', 'b', 'c'
}

jsbin

Cependant, s'il vous plaît faites-moi une faveur et évitez héritage. :-)


138
2017-08-08 12:20



Pas de méthodes natives, mais lodash # mapValues fera le travail avec brio

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }

83
2017-12-18 16:46



C'est assez facile d'en écrire un:

Object.map = function(o, f, ctx) {
    ctx = ctx || this;
    var result = {};
    Object.keys(o).forEach(function(k) {
        result[k] = f.call(ctx, o[k], k, o); 
    });
    return result;
}

avec un exemple de code:

> o = { a: 1, b: 2, c: 3 };
> r = Object.map(o, function(v, k, o) {
     return v * v;
  });
> r
{ a : 1, b: 4, c: 9 }

NB: cette version vous permet également de définir (optionnellement) this contexte pour le rappel, tout comme le Array méthode.

MODIFIER - changé pour supprimer l'utilisation de Object.prototype, pour s'assurer qu'il ne soit pas en conflit avec une propriété existante nommée map sur l'objet.


53
2018-02-11 10:52



Vous pourriez utiliser Object.keys et alors forEach sur le tableau de clés retourné:

var myObject = { 'a': 1, 'b': 2, 'c': 3 },
    newObject = {};
Object.keys(myObject).forEach(function (key) {
    var value = myObject[key];
    newObject[key] = value * value;
});

Ou d'une manière plus modulaire:

function map(obj, callback) {
    var result = {};
    Object.keys(obj).forEach(function (key) {
        result[key] = callback.call(obj, obj[key], key, obj);
    });
    return result;
}

newObject = map(myObject, function(x) { return x * x; });

Notez que Object.keys retourne un tableau contenant uniquement les propriétés énumérables de l'objet, ainsi il se comporte comme un for..in boucle avec un hasOwnProperty vérifier.


20
2018-02-11 10:53



C'est bs droit, et tout le monde dans la communauté JS le sait. Là devraitêtre cette fonctionnalité:

const obj1 = {a:4, b:7};
const obj2 = Object.map(obj1, (k,v) => v + 5);

console.log(obj1); // {a:4, b:7}
console.log(obj2); // {a:9, b:12}

voici l'implémentation naïve:

Object.map = function(obj, fn, ctx){

    const ret = {};

    Object.keys(obj).forEach(function(k){
        ret[k] = fn.call(ctx || null, k, obj[k]);
    });

    return ret;
};

il est super ennuyeux d'avoir à le faire soi-même tout le temps;)

Si vous voulez quelque chose d'un peu plus sophistiqué, qui n'interfère pas avec la classe Object, essayez ceci:

let map = function (obj, fn, ctx) {
  return Object.keys(obj).reduce((a, b) => {
    a[b] = fn.call(ctx || null, b, obj[b]);
    return a;
  }, {});
};


const x = map({a: 2, b: 4}, (k,v) => {
    return v*2;
});

mais il est sûr d'ajouter cette fonction de carte à l'objet, ne pas ajouter à Object.prototype.

Object.map = ... // fairly safe
Object.prototype.map ... // not ok

14
2017-10-12 21:01



Je suis venu ici pour trouver et répondre à un mappage d'un objet sur un tableau et j'ai obtenu cette page. Au cas où vous êtes venu ici à la recherche de la même réponse que moi, voici comment vous pouvez mapper et objecter à un tableau.

Vous pouvez utiliser map pour renvoyer un nouveau tableau de l'objet comme ceci:

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});

11
2017-10-21 17:39



La réponse acceptée a deux inconvénients:

  • Il fait un mauvais usage Array.prototype.reduce, car réduire signifie changer la structure d'un type composite, ce qui n'arrive pas dans ce cas.
  • Ce n'est pas particulièrement réutilisable

Une approche fonctionnelle ES6 / ES2015

Veuillez noter que toutes les fonctions sont définies sous forme curry.

// small, reusable auxiliary functions

const keys = o => Object.keys(o);

const assign = (...o) => Object.assign({}, ...o);

const map = f => xs => xs.map(x => f(x));

const mul = y => x => x * y;

const sqr = x => mul(x) (x);


// the actual map function

const omap = f => o => {
  o = assign(o); // A
  map(x => o[x] = f(o[x])) (keys(o)); // B
  return o;
};


// mock data

const o = {"a":1, "b":2, "c":3};


// and run

console.log(omap(sqr) (o));
console.log(omap(mul(10)) (o));

  • En ligne A o est réaffecté. Puisque Javascript passe des valeurs de référence en partageant, une copie superficielle de o est généré. Nous sommes maintenant capables de muter o dans omap sans mutation o dans la portée parentale.
  • Dans la ligne B mapLa valeur de retour est ignorée, car map effectue une mutation de o. Puisque cet effet secondaire reste dans omap et n'est pas visible dans la portée parentale, elle est totalement acceptable.

Ce n'est pas la solution la plus rapide, mais déclarative et réutilisable. Voici la même implémentation en une ligne, succincte mais moins lisible:

const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);

Addendum - pourquoi les objets ne sont-ils pas itérables par défaut?

ES2015 a spécifié l'itérateur et les protocoles itératifs. Mais les objets ne sont toujours pas itérables et donc non cartographiables. La raison en est le mélange des données et du niveau de programme.


11
2017-08-03 20:45