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.)
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.
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]})));
Outra versão usando o reduce:
let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {});
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);
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.
,{})
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;
};
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) {...}
.
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'
}
No entanto, por favor, faça-me um favor e evite a herança . :-)
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 }
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.
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
.
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
Você pode usar o mapa para devolver uma nova matriz do objecto assim:
var newObject = Object.keys(myObject).map(function(key) {
return myObject[key];
});
- 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 mutaro
dentro deomap
sem mutaro
no âmbito dos pais. - na linha B
map
o valor de retorno é ignorado, porquemap
realiza uma mutação deo
. Uma vez que este efeito colateral permanece dentro deomap
e não é visível no escopo pai, é totalmente aceitá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 .
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.
Versão mínima (es6):
Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
-
m
, a funçãodo mapeamento dá – Lhe a oportunidade de transformar o elemento de entrada antes de ... -
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 }
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]
.
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 .
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;
});
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}
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 }
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 immutable
e talvez queira ler os documentos:
Se você está interessado em map
ping 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 }
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];
}
}
}
}
}
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)
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();
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>
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}
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>
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.
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))