Pregunta función de mapa para objetos (en lugar de matrices)


Tengo un objeto:

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

Estoy buscando un método nativo, similar a Array.prototype.map que se usaría de la siguiente manera:

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

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

¿JavaScript tiene tal map función para los objetos? (Quiero esto para Node.JS, por lo que no me importan los problemas entre navegadores).


574
2018-02-11 10:41


origen


Respuestas:


No hay nativo map al Object objeto, pero ¿qué tal esto?

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

console.log(myObject);

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

Pero podrías iterar fácilmente sobre un objeto usando for ... in:

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

Actualizar

Mucha gente menciona que los métodos anteriores no devuelven un objeto nuevo, sino que operan en el objeto mismo. Para el caso, quería agregar otra solución que devuelva un nuevo objeto y deje el objeto original tal como 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 reduce una matriz a un único valor fusionando un poco el valor anterior con el actual. La cadena se inicializa con un objeto vacío {}. En cada iteración, una nueva clave de myObject se agrega con su cuadrado como valor.


830
2018-02-11 10:52



¿Qué tal un trazador de líneas con asignación de variable inmediata en JS simple (ES6 / ES2015)?

Haciendo uso de operador de propagación y nombre de clave computada sintaxis:

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

jsbin

Otra versión que usa reduce:

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

jsbin

Primer ejemplo como una función:

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 quieres mapear un objeto anidado recursivamente en un funcional estilo, se puede hacer así:

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

O más imperativamente, así:

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

Ya que ES7 / ES2016 puedes usar Object.entries en lugar de Object.keys p.ej. Me gusta esto:

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


Propiedades heredadas y la cadena de prototipos:

En alguna situación rara, es posible que necesite mapear un clase-como objeto que tiene propiedades de un objeto heredado en su cadena de prototipos. En esos casos Object.keys() no funcionará, porque Object.keys() no enumera heredado propiedades. Si necesita hacer un mapa heredado propiedades, debes usar for (key in myObj) {...}.

Aquí hay un ejemplo de un objeto que hereda las propiedades de otro objeto y cómo Object.keys() no funciona en tal escenario.

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

Sin embargo, hazme un favor y evita herencia. :-)


138
2017-08-08 12:20



Sin métodos nativos, pero lodash # mapValues hará el trabajo brillantemente

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

83
2017-12-18 16:46



Es bastante fácil escribir uno:

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

con código de ejemplo:

> 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: esta versión también le permite (opcionalmente) configurar el this contexto para la devolución de llamada, al igual que el Array método.

EDITAR - cambiado para eliminar el uso de Object.prototype, para asegurarse de que no choca con ninguna propiedad existente llamada map en el objeto.


53
2018-02-11 10:52



Podrías usar Object.keys y entonces forEach sobre la matriz devuelta de claves:

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

O de una manera más modular:

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

Tenga en cuenta que Object.keys devuelve una matriz que contiene solo las propiedades enumerables del objeto, por lo que se comporta como un for..in lazo con un hasOwnProperty comprobar.


20
2018-02-11 10:53



Esto es directamente bs, y todos en la comunidad JS lo saben. Ahí deberíasea ​​esta funcionalidad:

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}

aquí está la implementación ingenua:

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

es muy molesto tener que implementar esto usted mismo todo el tiempo;)

Si quieres algo un poco más sofisticado, que no interfiera con la clase Object, prueba esto:

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

pero es seguro agregar esta función de mapa a Object, simplemente no agregue Object.prototype.

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

14
2017-10-12 21:01



Vine aquí buscando encontrar y responder para mapear un objeto a una matriz y obtuve esta página como resultado. En caso de que vinieras aquí buscando la misma respuesta que yo, aquí es cómo puedes mapear y objetar a una matriz.

Puede usar el mapa para devolver una nueva matriz del objeto de esta manera:

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

11
2017-10-21 17:39



La respuesta aceptada tiene dos inconvenientes:

  • Hace mal uso Array.prototype.reduce, porque reducir significa cambiar la estructura de un tipo compuesto, lo que no sucede en este caso.
  • No es particularmente reutilizable

Un enfoque funcional ES6 / ES2015

Tenga en cuenta que todas las funciones se definen en currículum.

// 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 la línea A o es reasignado Como Javascript pasa valores de referencia compartiendo, una copia superficial de o es generado. Ahora podemos mutar o dentro omap sin mutar o en el alcance principal
  • En la línea B mapSe ignora el valor de retorno porque map realiza una mutación de o. Dado que este efecto secundario permanece dentro de omap y no es visible en el alcance principal, es totalmente aceptable.

Esta no es la solución más rápida, sino declarativa y reutilizable. Aquí está la misma implementación que una línea, sucinta pero menos legible:

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

Adición: ¿por qué los objetos no son iterables por defecto?

ES2015 especificó el iterador y los protocolos iterables. Pero los objetos aún no son iterables y por lo tanto no se pueden mapear. La razón es la mezcla de datos y el nivel del programa.


11
2017-08-03 20:45



Para un rendimiento máximo.

Si su objeto no cambia a menudo pero necesita repetirse, sugiero usar un mapa nativo como caché.

// example object
var obj = {a: 1, b: 2, c: 'something'};

// caching map
var objMap = new Map(Object.entries(obj));

// fast iteration on Map object
objMap.forEach((item, key) => {
  // do something with an item
  console.log(key, item);
});

Object.entries ya funciona en Chrome, Edge, Firefox y beta Opera por lo que es una característica a prueba de futuro. Es de ES7 así que colócala https://github.com/es-shims/Object.entries para IE donde no funciona.


8
2017-10-03 10:22



Versión mínima (es6):

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})

6
2017-07-17 22:58