matriz - objetos literales javascript
Función de mapa para objetos(en lugar de matrices) (24)
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 }
¿Tiene JavaScript una función de map
para objetos? (Quiero esto para Node.JS, por lo que no me importan los problemas entre los navegadores).
¿Qué tal un trazador de líneas con asignación de variable inmediata en JS plano ( ES6 / ES2015 )?
Haciendo uso del operador de propagación y la sintaxis de nombre de clave calculada :
let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]})));
Otra versión usando reducir:
let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {});
Primer ejemplo como 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);
Si desea asignar un objeto anidado recursivamente en un estilo funcional , 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.
,{})
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;
};
Desde ES7 / ES2016 puede usar Object.entries
lugar de Object.keys
por ejemplo, de esta forma:
let newObj = Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * v})));
Propiedades heredadas y la cadena prototipo:
En una situación rara, es posible que deba asignar un objeto similar a una clase que contenga las propiedades de un objeto heredado en su prototype-chain . En tales casos, Object.keys()
no funcionará, porque Object.keys()
no enumera las propiedades heredadas . Si necesita asignar propiedades heredadas , debe 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 ese 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''
}
Sin embargo, por favor hazme un favor y evita la herencia . :-)
Basado en la respuesta de @Amberlamps, aquí hay una función de utilidad (como comentario, se veía feo)
function mapObject(obj, mapFunc){
return Object.keys(obj).reduce(function(newObj, value) {
newObj[value] = mapFunc(obj[value]);
return newObj;
}, {});
}
y el uso es:
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}
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 this
contexto para la devolución de llamada, al igual que el método Array
.
EDITAR : se modificó para eliminar el uso de Object.prototype
, para garantizar que no Object.prototype
con ninguna propiedad existente denominada map
en el objeto.
Específicamente quería usar la misma función que estaba usando para arreglos para un solo objeto, y quería mantenerlo simple. Esto funcionó para mí:
var mapped = [item].map(myMapFunction).pop();
Esta respuesta se ha reescrito para demostrar un potente combinador, mapReduce
, que nos permite pensar en nuestra transformación de una manera diferente.
-
m
, la función de mapeo : te da la oportunidad de transformar el elemento entrante antes de ... -
r
, la función reductora - esta función combina el acumulador con el resultado del elemento mapeado
Intuitivamente, mapReduce
crea un nuevo reductor que podemos enchufar directamente en Array.prototype.reduce
. Pero, lo que es más importante, podemos implementar nuestra omap
implementación del funtor de objetos utilizando el Object.assign
objeto, Object.assign
y {}
.
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 }
Tenga en cuenta que la única parte del programa que realmente tuvimos que escribir es la implementación del mapeo en sí,
k => ({ [k]: f (o [k]) }
Lo que dice, dado un objeto conocido o
y alguna clave k
, construye un objeto y cuya propiedad k
es el resultado de llamar a f
en el valor de la clave, o [k]
.
Podemos vislumbrar el potencial de secuenciación de mapReduce
si primero hacemos un resumen de 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
)
, {}
)
Todo funciona de la misma manera, pero omap
se puede definir omap
en un nivel superior. Por supuesto, el nuevo Object.entries
hace que esto parezca tonto, pero el ejercicio sigue siendo importante para el alumno.
No verás todo el potencial de mapReduce
aquí, pero comparto esta respuesta porque es interesante ver cuántos lugares se pueden aplicar. Si está interesado en cómo se deriva y otras formas en que podría ser útil, consulte esta respuesta .
Esto es directamente bs, y todos en la comunidad JS lo saben. Debería haber 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 super molesto tener que implementar esto usted mismo todo el tiempo;)
Si quieres algo un poco más sofisticado, que no interfiera con la clase de Objeto, 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 Objeto, simplemente no lo agregue a Object.prototype.
Object.map = ... // fairly safe
Object.prototype.map ... // not ok
Hey escribió una pequeña función de mapeador que podría ayudar.
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];
}
}
}
}
}
La map function
no existe en el Object.prototype
sin embargo, puede emularlo así
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;
});
La respuesta aceptada tiene dos inconvenientes:
- Hace un mal uso de
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 forma de 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 la línea A
o
se reasigna. Dado que Javascript pasa los valores de referencia al compartir , se genera una copia superficial deo
. Ahora podemos mutaro
dentro deomap
sin mutaro
en el ámbito principal. - En la línea B, el valor de retorno del
map
se ignora, porque elmap
realiza una mutación deo
. Dado que este efecto secundario permanece dentro deomap
y no es visible en el ámbito principal, es totalmente aceptable.
Esta no es la solución más rápida, sino una declarativa y reutilizable. Esta es 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);
Addendum: ¿por qué los objetos no son iterables de forma predeterminada?
ES2015 especifica el iterador y los protocolos iterables. Pero los objetos todavía no son iterables y, por lo tanto, no son asignables. La razón es la mezcla de datos y nivel de programa .
Me encontré con esto como un primer elemento en una búsqueda de Google que intentaba aprender a hacer esto, y pensé que compartiría con otros folsk encontrando esto recientemente la solución que encontré, que usa el paquete npm inmutable.
Creo que es interesante compartirlo porque inmutable usa la situación EXACTA del OP en su propia documentación; el siguiente no es mi propio código, sino que se extrae de la documentación actual de inmutable-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 }
No es que Seq tenga otras propiedades ("Seq describe una operación perezosa, lo que les permite encadenar de manera eficiente el uso de todos los métodos de recopilación de orden superior (como el mapa y el filtro) al no crear colecciones intermedias") y que algunos otros datos inmutables de js Las estructuras también pueden hacer el trabajo de manera bastante eficiente.
Cualquier persona que use este método, por supuesto, tendrá que npm install immutable
y querrá leer la documentación:
Necesitaba una versión que permitiera modificar las claves también (según las respuestas de @Amberlamps y @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"}
}
Edición: ligero cambio para pasar en el objeto inicial {}. Permite que sea [] (si las claves son enteros)
No hay métodos nativos, pero lodash#mapValues hará el trabajo de manera brillante
_.mapValues({ ''a'': 1, ''b'': 2, ''c'': 3} , function(num) { return num * 3; });
// → { ''a'': 3, ''b'': 6, ''c'': 9 }
No hay un map
nativo al Object
Objeto, pero ¿qué hay de esto?
Object.keys(myObject).map(function(key, index) {
myObject[key] *= 2;
});
console.log(myObject);
// => { ''a'': 2, ''b'': 4, ''c'': 6 }
Pero podría iterar fácilmente sobre un objeto usando for ... in
:
for(var key in myObject) {
if(myObject.hasOwnProperty(key)) {
myObject[key] *= 2;
}
}
Actualizar
Muchas personas mencionan que los métodos anteriores no devuelven un objeto nuevo, sino que operan en el objeto en sí. En ese sentido, quería agregar otra solución que devuelva un objeto nuevo y deje el objeto original tal como está:
// 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
reduce una matriz a un solo valor al fusionar un poco el valor anterior con el actual. La cadena se inicializa con un objeto vacío {}
. En cada iteración se agrega una nueva clave de myObject
con su cuadrado como valor.
Para el máximo rendimiento.
Si su objeto no cambia con frecuencia pero necesita repetirse, le 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, por lo tanto, polyfill it https://github.com/es-shims/Object.entries para IE donde no funciona.
Podría usar Object.keys
y luego forEach
sobre la matriz de claves devuelta:
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 propias propiedades enumerables del objeto, por lo que se comporta como un bucle for..in
con una verificación hasOwnProperty
.
Puede utilizar un bucle for-in simple a través de pares ordenados. hasOwnProperty()
porque creaste tres propiedades (con valores) para tu objeto. El primer método no crea un mapa. En su lugar, simplemente aplica la función a un solo elemento, lo que puede acelerar enormemente la ejecución en muchas situaciones.
El segundo método crea un mapa de manera similar al primero, pero probablemente sea más lento que algunas otras respuestas aquí.
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>
Si alguien estaba buscando una solución simple que asigne un objeto a un nuevo objeto oa una 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]))
);
Es posible que esto no funcione para todos los objetos o todas las funciones de mapeo, pero funciona para objetos llanos y simples y funciones de mapeo sencillas, que es todo lo que necesitaba.
Si está interesado en hacer ping de map
no solo los valores sino también las claves, he escrito Object.map(valueMapper, keyMapper)
, que se comporta de esta manera:
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 }
Versión mínima (es6):
Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
Vine aquí buscando y respondiendo para asignar un objeto a una matriz y, como resultado, obtuve esta página. En caso de que viniera aquí en busca de la misma respuesta que yo, aquí es cómo puede asignar y objetar a una matriz.
Puede usar map para devolver una nueva matriz desde el objeto de la siguiente manera:
var newObject = Object.keys(myObject).map(function(key) {
return myObject[key];
});
puede usar el método de map
y forEach
en los arreglos, pero si desea usarlo en Object
, puede usarlo con un giro como el siguiente:
Utilizando Javascript (ES6)
var obj = { ''a'': 2, ''b'': 4, ''c'': 6 };
Object.entries(obj).map( v => obj[v[0]] *= v[1] );
console.log(obj); //it will log as {a: 4, b: 16, c: 36}
var obj2 = { ''a'': 4, ''b'': 8, ''c'': 10 };
Object.entries(obj2).forEach( v => obj2[v[0]] *= v[1] );
console.log(obj2); //it will log as {a: 16, b: 64, c: 100}
Usando jQuery
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}
O puede usar otros bucles también como el método $.each
como se muestra a continuación:
$.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>
Use el nuevo _.transform () para transformar el objeto .
Otra forma de verlo es utilizar una función de json stringify personalizada que también puede funcionar en objetos profundos. Esto podría ser útil si tiene la intención de publicarlo en el servidor de todos modos 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))
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 }