função de mapa para objectos (em vez de arrays)

tenho um objecto:

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

Estou à procura de um método nativo, semelhante a {[2] } que seria usado da seguinte forma:

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

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

o JavaScript tem tal função map para objectos? (I want this for Node.JS, por isso não me importo com problemas de navegação cruzada.)

Author: Will Ness, 2013-02-11

24 answers

Não há nenhum nativo map para o objeto Object, mas que tal isto:

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

console.log(myObject);

// => { 'a': 2, 'b': 4, 'c': 6 }
Mas você poderia facilmente iterar sobre um objeto usando for ... in:
for(var key in myObject) {
    if(myObject.hasOwnProperty(key)) {
        myObject[key] *= 2;
    }
}

Actualizar

Muitas pessoas estão mencionando que os métodos anteriores não retornam um novo objeto, mas operam sobre o próprio objeto. Por falar nisso, eu queria adicionar outra solução que retornasse um novo objeto e deixasse o objeto original como ele é:
// returns a new object with the values at each key mapped using mapFn(value)
function objectMap(object, mapFn) {
    return Object.keys(object).reduce(function(result, key) {
        result[key] = mapFn(object[key])
        return result
    }, {})
}

var newObject = objectMap(myObject, function(value) {
  return value*2
})

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

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

Array.prototype.reduce reduz um array para um único valor, fundindo de alguma forma o valor anterior com o actual. A cadeia é inicializada por um objeto vazio {}. Em cada iteração é adicionada uma nova chave de myObject com o seu quadrado como valor.

 892
Author: Amberlamps, 2018-08-24 00:47:00
Que tal um transatlântico com atribuição variável imediata em JS simples ([15]}ES6 / ES2015 ) ?

Fazendo uso de operador de spread e nome da chave computado sintaxe:

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

Jsbin

Outra versão usando o reduce:

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

Jsbin

Primeiro exemplo em função:

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

Se quiser mapear um objecto aninhado recursivamente num funcional estilo, pode ser feito assim:

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 mais imperativamente, assim:
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

Desde ES7 / ES2016 podes usar Object.entries em vez de Object.keys por exemplo, assim:

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


Propriedades hereditárias e a cadeia de protótipos:

Em alguma situação rara pode ser necessário mapear um objecto semelhante a uma classe que possui propriedades de um objecto hereditário object on its prototype-chain . Em tais casos Object.keys() não funcionará, porque Object.keys() não enumera propriedades herdadas. Se precisar de mapear propriedades hereditárias, deve utilizar for (key in myObj) {...}.

Aqui está um exemplo de um objeto que herda as propriedades de outro objeto e como Object.keys() não funciona em tal cenário.
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

No entanto, por favor, faça-me um favor e evite a herança . :-)

 154
Author: Rotareti, 2018-03-30 06:15:14

Nenhum método nativo, mas lodash#mapValues fará o trabalho brilhantemente

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
 91
Author: Mario, 2013-12-18 16:46:13
É muito fácil escrever um.
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;
}

Com o código de exemplo:

> 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 versão também lhe permite (opcionalmente) definir o contexto this para a chamada de resposta, tal como o método Array.

EDIT - alterado para remover o uso de Object.prototype, para garantir que não entra em conflito com nenhuma propriedade existente chamada map no objecto.

 55
Author: Alnitak, 2016-08-08 12:08:09

Podias usar Object.keys e então forEach Sobre a lista de chaves devolvidas:

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

Ou de uma forma mais 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; });

Note que Object.keys devolve um array contendo apenas as próprias propriedades enumeráveis do objecto, por isso comporta-se como um laço for..in com uma verificação hasOwnProperty.

 20
Author: Mattias Buelens, 2013-02-11 10:59:12
Isto é treta, e todos na comunidade JS sabem disso. deve Ser esta funcionalidade:
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}

Aqui está a implementação ingênua:

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;
};
É super irritante ter que implementar isso você mesmo o tempo todo;) Se queres algo um pouco mais sofisticado, que não interfira com a classe de objectos, tenta isto:
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;
});

Mas é Seguro adicionar esta função de mapa ao objecto, apenas não adicione Objecto.prototipo.

Object.map = ... // fairly safe
Object.prototype.map ... // not ok
 14
Author: Alexander Mills, 2018-02-06 20:05:28
Vim aqui à procura e resposta para mapear um objecto para uma matriz e consegui esta página como resultado. No caso de você vir aqui procurando a mesma resposta que eu era, aqui está como você pode mapear e objetar a uma matriz.

Você pode usar o mapa para devolver uma nova matriz do objecto assim:

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});
 12
Author: JoeTron, 2016-08-09 17:13:17
A resposta aceite tem duas desvantagens:
  • ele usa mal {[[2]}, porque reduzir significa mudar a estrutura de um tipo composto, o que não acontece neste caso.
  • Não é particularmente reutilizável.

Uma abordagem funcional ES6 / ES2015

Por favor, note que todas as funções são definidas em caril.

// 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));
  • na linha a {[3] } é recolocada. Desde O Javascript passa os valores de referência partilhando , gera-se uma cópia pouco profunda de o. Agora somos capazes de mutar o dentro de omap sem mutar o no âmbito dos pais.
  • na linha B map o valor de retorno é ignorado, porque map realiza uma mutação de o. Uma vez que este efeito colateral permanece dentro de omap e não é visível no escopo pai, é totalmente aceitável.
Esta não é a solução mais rápida, mas uma solução declarativa e reutilizável. Aqui está o mesmo implementação como uma linha única, sucinta mas menos legível:
const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);

Adenda-porque é que os objectos não são iteráveis por omissão?

Os ES2015 especificaram os protocolos iterador e iterável. Mas os objetos ainda não são iteráveis e, portanto, não mapeáveis. a razão é a mistura de dados e nível de programa .

 11
Author: ftor, 2017-05-23 11:47:32

Para o desempenho máximo.

Se o seu objecto não muda muitas vezes, mas precisa de ser iterado em muitas vezes, sugiro que use um mapa nativo como um cache.

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

Objecto.as entradas já funcionam no Chrome, Edge, Firefox e beta Opera, por isso é um recurso à prova de futuro. É do ES7, por isso o polifillam. https://github.com/es-shims/Object.entries para IE onde não funciona.

 8
Author: Pawel, 2016-10-03 10:22:10

Versão mínima (es6):

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
 6
Author: Yukulélé, 2017-10-02 07:46:37
Esta resposta foi reescrita para demonstrar um poderoso combinador, o que nos permite pensar na nossa transformação de uma forma diferente.
  1. m, a funçãodo mapeamento dá – Lhe a oportunidade de transformar o elemento de entrada antes de ...
  2. r, a função redutora - esta função combina o acumulador com o resultado do elemento mapeado

Intuitivamente, mapReduce cria um novo redutor que podemos ligar directamente a Array.prototype.reduce. Mas mais importante, Nós podemos implementar nossa implementação do functor objeto omap claramente utilizando o monóide objeto, Object.assign e {}.

const identity = x =>
  x
  
const first = (a, b) =>
  a
  
const mapReduce = (m = identity, r = first) =>
  (acc, x) => r (acc, m (x))
  
const omap = (o = {}, f = identity) =>
  Object.keys (o)
    .reduce ( mapReduce ( k => ({ [k]: f (o [k]) })  // Object.map
                        , Object.assign              // Object.concat
                        )
            , {}                                     // Object.empty
            )
          
const square = x =>
  x * x
  
const data =
  { a : 1, b : 2, c : 3 }
  
console.log (omap (data, square))
// { a : 1, b : 4, c : 9 }
Reparem que a única parte do programa que tínhamos de escrever era a implementação do mapeamento em si.
k => ({ [k]: f (o [k]) }

Que diz, dado um objecto conhecido o e alguma chave k, construir um objecto e cuja propriedade {[12] } é o resultado de chamar f sobre o valor da chave, o [k].

Temos um vislumbre do potencial sequenciador de mapReduce Se primeiro abstrarmos oreduce
const oreduce = (o, f = first, acc = {}) =>
  Object.keys (o)
    .reduce ( mapReduce ( k => [ k, o[k] ]
                        , f
                        )
            , acc
            )

const omap = (o = {}, f = identity) =>
  oreduce ( o
          , mapReduce ( ([ k, v ]) => ({ [k]: f (v) })
                      , Object.assign
                      )
          , {}
          )

Tudo funciona da mesma forma, mas omap pode ser definido a um nível mais alto agora. É claro que o novo Object.entries faz com que isto pareça ridículo, mas o exercício ainda é importante para o aprendiz.

Você não vai ver todo o potencial de mapReduce aqui, mas eu partilho esta resposta porque é interessante ver quantos lugares ele pode ser aplicado. Se você está interessado em como ele é derivado e outras maneiras que poderia ser útil, por favor veja esta resposta .

 5
Author: user633183, 2018-02-06 21:01:14

O map function não existe no Object.prototype no entanto você pode emulá-lo assim

var myMap = function ( obj, callback ) {

    var result = {};

    for ( var key in obj ) {
        if ( Object.prototype.hasOwnProperty.call( obj, key ) ) {
            if ( typeof callback === 'function' ) {
                result[ key ] = callback.call( obj, obj[ key ], key, obj );
            }
        }
    }

    return result;

};

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

var newObject = myMap( myObject, function ( value, key ) {
    return value * value;
});
 3
Author: whitneyit, 2013-02-11 10:51:04

Baseado na resposta de @ Amber tetos, aqui está uma função de utilidade pública (como um comentário que parecia feio)

function mapObject(obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, value) {
        newObj[value] = mapFunc(obj[value]);
        return newObj;
    }, {});
}

E a utilização é:

var obj = {a:1, b:3, c:5}
function double(x){return x * 2}

var newObj = mapObject(obj, double);
//=>  {a: 2, b: 6, c: 10}
 2
Author: yonatanmn, 2016-02-02 13:30:08
var myObject = { 'a': 1, 'b': 2, 'c': 3 };


Object.prototype.map = function(fn){
    var oReturn = {};
    for (sCurObjectPropertyName in this) {
        oReturn[sCurObjectPropertyName] = fn(this[sCurObjectPropertyName], sCurObjectPropertyName);
    }
    return oReturn;
}
Object.defineProperty(Object.prototype,'map',{enumerable:false});





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


// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
 2
Author: Dmitry Ragozin, 2016-11-16 12:32:18

Eu encontrei isso como um primeiro item em uma pesquisa do Google tentando aprender a fazer isso, e pensei que eu iria compartilhar para outros folsk encontrar esta recentemente a solução que eu encontrei, que usa o pacote npm imutável.

Eu acho que é interessante compartilhar porque a imutável usa a situação exata da OP em sua própria documentação - o seguinte não é o meu próprio código, mas retirado da atual documentação imutável-js:

const { Seq } = require('immutable')
const myObject = { a: 1, b: 2, c: 3 }
Seq(myObject).map(x => x * x).toObject();
// { a: 1, b: 4, c: 9 } 
([3]) não que Seq tenha outras propriedades ("Seq descreve uma operação preguiçosa, permitindo-lhes o uso eficiente em cadeia de todos os métodos de coleta de ordem superior (como mapa e filtro) por não criar coleções intermediárias") e que algumas outras estruturas de dados imutáveis-js também pode fazer o trabalho de forma bastante eficiente.

Qualquer pessoa que utilize este método terá de npm install immutablee talvez queira ler os documentos:

Https://facebook.github.io/immutable-js/

 2
Author: alex.h, 2018-01-07 22:02:46

Se você está interessado em mapping não só valores, mas também Chaves, eu escrevi Object.map(valueMapper, keyMapper), que se comporta assim:

var source = { a: 1, b: 2 };
function sum(x) { return x + x }

source.map(sum);            // returns { a: 2, b: 4 }
source.map(undefined, sum); // returns { aa: 1, bb: 2 }
source.map(sum, sum);       // returns { aa: 2, bb: 4 }
 1
Author: MattiSG, 2015-03-16 13:33:26
Escrevi uma pequena função cartográfica que pode ajudar.
    function propertyMapper(object, src){
         for (var property in object) {   
           for (var sourceProp in src) {
               if(property === sourceProp){
                 if(Object.prototype.toString.call( property ) === '[object Array]'){
                   propertyMapper(object[property], src[sourceProp]);
                   }else{
                   object[property] = src[sourceProp];
                }
              }
            }
         }
      }
 1
Author: Zak, 2016-01-22 13:55:54

Eu precisava de uma versão que permitisse modificar as teclas também (com base nas lâmpadas @Amber e nas respostas @yonatanmn);

var facts = [ // can be an object or array - see jsfiddle below
    {uuid:"asdfasdf",color:"red"},
    {uuid:"sdfgsdfg",color:"green"},
    {uuid:"dfghdfgh",color:"blue"}
];

var factObject = mapObject({}, facts, function(key, item) {
    return [item.uuid, {test:item.color, oldKey:key}];
});

function mapObject(empty, obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, key) {
        var kvPair = mapFunc(key, obj[key]);
        newObj[kvPair[0]] = kvPair[1];
        return newObj;
    }, empty);
}

FactObject=

{
"asdfasdf": {"color":"red","oldKey":"0"},
"sdfgsdfg": {"color":"green","oldKey":"1"},
"dfghdfgh": {"color":"blue","oldKey":"2"}
}

Editar: ligeira alteração a passar no objecto inicial {}. Permite que seja [] (se as chaves forem inteiros)

 1
Author: Symmetric, 2016-08-13 23:51:13

Eu queria especificamente usar a mesma função que eu estava usando para arrays para um único objeto, e queria mantê-lo simples. Isto funcionou comigo.

var mapped = [item].map(myMapFunction).pop();
 1
Author: Jason Norwood-Young, 2016-10-03 09:20:04

Pode usar um simples laço for-in através de pares ordenados. Eu usei hasOwnProperty() porque você criou três propriedades (com valores) para o seu objecto. O primeiro método não cria um mapa. Em vez disso, ele simplesmente aplica a função a um único elemento, que pode acelerar muito a execução em muitas situações.

O segundo método cria um mapa da mesma forma que o primeiro, mas é provavelmente mais lento do que algumas outras respostas. aqui.

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

//Doesn't create a map, just applies the function to a specific element
function getValue(key) {
  for (var k in myObject) {
    if (myObject.hasOwnProperty(key)) {
      var value = myObject[key]
      return value * value; //stops iteration
    }
  }
}

//creates a map (answers question, but above function is better in some situations)
var newObject = {};
makeMap();

function makeMap() {
    for (var k in myObject) {
        var value = myObject[k];
        newObject[k] = value * value;
    }
}

console.log(newObject); //mapped array
Input: <input id="input" value="" placeholder="a, b or c"><br>
Output:<input id="output"><br>
<button onclick="output.value=getValue(input.value)" >Get value</button>
 1
Author: Victor Stoddard, 2018-03-04 02:52:25

Pode usar o método map em matrizes, mas se quiser usá-lo em Object então pode usá-lo com torção como em baixo:

var ob = { 'a': 2, 'b': 4, 'c': 6 };
$.map(ob, function (val, key) {
   ob[key] *= val;
});
console.log(ob) //it will log as {a: 4, b: 16, c: 36}

Ou pode usar outros laços também como $.each o método abaixo, por exemplo:

$.each(ob,function (key, value) {
  ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}
 0
Author: Haritsinh Gohil, 2018-09-29 12:03:26

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

const result = _.transform(orig, (r, v, k) => r[k.trim()] = v * 2);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Utilizar novo.transformar () para transformar objeto.

 -1
Author: Parth Raval, 2018-04-09 11:12:31

Se alguém estivesse à procura de uma solução simples que mapeie um objecto para um novo objecto ou para uma matriz:

// Maps an object to a new object by applying a function to each key+value pair.
// Takes the object to map and a function from (key, value) to mapped value.
const mapObject = (obj, fn) => {
    const newObj = {};
    Object.keys(obj).forEach(k => { newObj[k] = fn(k, obj[k]); });
    return newObj;
};

// Maps an object to a new array by applying a function to each key+value pair.
// Takes the object to map and a function from (key, value) to mapped value.
const mapObjectToArray = (obj, fn) => (
    Object.keys(obj).map(k => fn(k, obj[k]))
);

Isto pode não funcionar para todos os objectos ou todas as funções de mapeamento, mas funciona para objectos rasos e simples funções de mapeamento que é tudo o que eu precisava.

 -1
Author: tailattention, 2018-05-22 23:41:00

Uma tomada diferente é usar uma função JSON stringify personalizada que também pode trabalhar em objetos profundos. Isso pode ser útil se você pretende postá-lo no servidor de qualquer maneira como json

const obj = { 'a': 1, 'b': 2, x: {'c': 3 }}
const json = JSON.stringify(obj, (k, v) => typeof v === 'number' ? v * v : v)

console.log(json)
console.log('back to json:', JSON.parse(json))
 -1
Author: Endless, 2018-07-31 15:21:04