Problem
I have a target:
myObject = { 'a': 1, 'b': 2, 'c': 3 }
I’m seeking for a native method that works in the same way as Array.prototype. The following is a map that might be used:
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
Is there a map method for objects in JavaScript? (I don’t care about cross-browser compatibility because this is for Node.JS.)
Asked by Randomblue
Solution #1
Although there is no native map to the Object object, consider this:
However, you can quickly iterate through an object with for… in:
Update
Many people have pointed out that the previous methods do not yield a new object, but instead work on the existing object. In fact, I’d want to provide a solution that yields a new object while leaving the original object alone:
By merging the previous and current values, Array.prototype.reduce reduces an array to a single value. An empty item is used to start the chain. Every iteration, a new key of myObject is created, with the key being twice as long as the value.
Update
There is a more elegant approach to represent objectMap with new ES6 features.
Answered by Amberlamps
Solution #2
How about a JS ES10 / ES2019 one-liner?
Using Object.entries() and Object.fromEntries() to create a list of entries:
let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]));
The following is the same thing written as a function:
function objMap(obj, func) {
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, func(v)]));
}
// To square each value you can call it like this:
let mappedObj = objMap(obj, (x) => x * x);
Recursion is also used to square nested objects in this function:
function objMap(obj, func) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) =>
[k, v === Object(v) ? objMap(v, func) : func(v)]
) ); } // To square each value you can call it like this: let mappedObj = objMap(obj, (x) => x * x);
You can’t use Objects.fromEntries in ES7 / ES2016, but you may get the same effect by using Object.assign in combination with spread operators and computed key names syntax:
let newObj = Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v})));
Object.entries isn’t allowed in ES6/ES2015, however you can use Object.keys instead:
let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]})));
In ES6, for…of loops were introduced, allowing for a more imperative style:
let newObj = {}
for (let [k, v] of Object.entries(obj)) {
newObj[k] = v * v;
}
Instead of Object.fromEntries and Object.assign you can also use reduce for this:
let newObj = Object.entries(obj).reduce((p, [k, v]) => ({ ...p, [k]: v * v }), {});
You might need to map a class-like object that holds properties of an inherited object on its prototype-chain in an unusual case. Object.keys() and Object.entries() will not work in this situation since they do not include the prototype chain.
You can use for (key in myObj)… if you need to map inherited properties.
Here’s an example of a circumstance like this:
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.
console.log(Object.entries(obj2)); // Prints: an empty Array.
for (let key in obj2) {
console.log(key); // Prints: 'a', 'b', 'c'
}
Please, please, please, please, please, please, please, please, please, please, please, please, please, please, please, please, please, please, please
Answered by Rotareti
Solution #3
There are no native methods, but lodash#mapValues will perform a fantastic job.
_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
Answered by Mario
Solution #4
It’s not difficult to write one:
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;
}
with example 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: Like the Array function, this version also allows you to (optionally) set the this context for the callback.
EDIT: Removed the use of Object.prototype to make sure it doesn’t conflict with any existing map property on the object.
Answered by Alnitak
Solution #5
You might use Object.keys and then forEach over the array of keys that was returned:
var myObject = { 'a': 1, 'b': 2, 'c': 3 },
newObject = {};
Object.keys(myObject).forEach(function (key) {
var value = myObject[key];
newObject[key] = value * value;
});
Alternatively, in a more modular manner:
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; });
It’s worth noting that Object.keys provides an array containing only the object’s own enumerable properties, thus it’s similar to a for..in loop with a hasOwnProperty check.
Answered by Mattias Buelens
Post is based on https://stackoverflow.com/questions/14810506/map-function-for-objects-instead-of-arrays