1768

I have an object:

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

I am looking for a native method, similar to Array.prototype.map that would be used as follows:

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

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

Does JavaScript have such a map function for objects? (I want this for Node.JS, so I don't care about cross-browser issues.)

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Randomblue
  • 112,777
  • 145
  • 353
  • 547
  • 1
    Most answers use `Object.keys`, which doesn't have any well-defined order. That can be problematic, I suggest using `Object.getOwnPropertyNames` instead. – Oriol Aug 09 '16 at 17:20
  • 3
    @Oriol are you sure about that? According to the MDN web docs, the ordering of array elements is consistent between `Object.keys` and `Object.getOwnPropertyNames`. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames – Bart Aug 15 '17 at 10:28
  • @Bart The order of `Object.keys` is implementation-dependent. See https://stackoverflow.com/a/30919039/1529630 – Oriol Apr 02 '18 at 18:50
  • 23
    @Oriol You're not supposed to rely on the order of keys in objects anyway, so its kinda irrelevant to the question - since he never specified that the order mattered to him. And if order mattered to him, he shouldn't be using an object at all anyway. – B T Aug 24 '18 at 00:44
  • someone please put some of these solutions in an npm package, with tests please – masterxilo Nov 19 '19 at 18:46

41 Answers41

2487

There is no native map to the Object object, but how about this:

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

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

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

But you could easily iterate over an object using for ... in:

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

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

console.log(myObject);
// { 'a': 2, 'b': 4, 'c': 6 }

Update

A lot of people are mentioning that the previous methods do not return a new object, but rather operate on the object itself. For that matter I wanted to add another solution that returns a new object and leaves the original object as it is:

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

// 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': 2, 'b': 4, 'c': 6 }

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

Array.prototype.reduce reduces an array to a single value by somewhat merging the previous value with the current. The chain is initialized by an empty object {}. On every iteration a new key of myObject is added with twice the key as the value.

Update

With new ES6 features, there is a more elegant way to express objectMap.

const objectMap = (obj, fn) =>
  Object.fromEntries(
    Object.entries(obj).map(
      ([k, v], i) => [k, fn(v, k, i)]
    )
  )
  
const myObject = { a: 1, b: 2, c: 3 }

console.log(objectMap(myObject, v => 2 * v)) 
Amberlamps
  • 39,180
  • 5
  • 43
  • 53
  • 1
    it also doesn't preserve the semantics of `Array.map`, which is supposed to return an array. – Alnitak Feb 11 '13 at 10:57
  • 1
    I've updated my answer - it seems (on re-reading the question) that the OP wants a _new_ object with the same keys, where the values were mutated with the supplied function, and not to modify the original object in place. – Alnitak Feb 11 '13 at 11:13
  • @Alnitak You're correct. But all you'd have to do to make this return an array is do `return mbObject[value] * 2;` inside the map callback. That way you don't modify the original object (blegh) and you get a nice new array with your modified data. – KhalilRavanna Jul 17 '14 at 18:25
  • 31
    @KhalilRavanna I think you've misread the code here - this answer isn't using `map` correctly because it isn't doing a `return` - it's abusing `map` as if it were a `forEach` call. If he actually _did_ `return myObject[value] * 2` then the result would be an _array_ containing the original values doubled, instead of an _object_ containing the original keys with doubled values, the latter clearly being what the OP asked for. – Alnitak Jul 17 '14 at 20:36
  • @Alnitak Right. To clarify, I was just addressing your original comment that this answer wasn't preserving the normal function of the `map` function. I wasn't addressing the fact that doing so would not answer OP's question. Consider my response a tangent from the original thread of question by OP. ;) – KhalilRavanna Jul 18 '14 at 16:01
  • @KhalilRavanna to clarify, what I meant (but omitted to say explicitly) is that it doesn't return a _new_ array ;-) – Alnitak Jul 18 '14 at 16:48
  • 1
    I agree with Alnitak. The use of `map` here is pointless when `forEach` would accomplish the same thing and make sense semantically. (And neither of your approaches produces a new object). – JLRishe Jan 27 '15 at 21:13
  • 3
    @Amberlamps your `.reduce` example is OK, but IMHO it still falls a long way short of the convenience of a `.map` for Objects, since your `.reduce` callback not only has to match the way that `.reduce` works, but also requires that `myObject` be available in the lexical scope. The latter in particular makes it impossible to just pass a function reference in the callback, requiring an in-place anonymous function instead. – Alnitak Mar 05 '15 at 23:03
  • What is *= a shorthand for? – NiCk Newman May 14 '15 at 17:57
  • 13
    TBH I would have rather upvoted an answer that only (or originally) provided the second solution presented in this answer. The first one works and is not necessarily wrong, but as others have stated, I don't like to see map being used that way. When I see map, my mind is automatically thinking "immutable data structure" – mejdev Feb 26 '16 at 17:04
  • How are we going to remove something from this map later? – SaraNa Apr 29 '16 at 23:05
  • 1
    This also gets a bit more terse with ES6. `const newObject = Object.keys(myObject).reduce((y, k) => Object.assign(y, { [k] : myObject[k] * myObject[k] }), {});` – kjv Jun 21 '16 at 19:42
  • The first code should use `forEach` instead of `map`. – Oriol Aug 09 '16 at 17:15
  • this is not the functional programming way to do it, see my answer instead. – Alexander Mills Oct 12 '16 at 22:14
  • 1
    First of all, OP did not ask for a solution in functional solution. Secondly, my updated solution is functional. – Amberlamps Oct 14 '16 at 06:43
  • your first solution is almost right, just add return – Altiano Gerung Dec 17 '16 at 08:44
  • for (const [key, value] of Object.entries(obj)) { console.log(`${key} ${value}`); // "a 5", "b 7", "c 9" } – Mahdi Bashirpour Jun 11 '18 at 13:25
  • 9
    `.map` is not the appropriate method to use when you aren't going to use the resulting mapped array - if you want side-effects only, such as in your first code, you should most definitely use `forEach` instead. – CertainPerformance Dec 03 '18 at 07:15
  • 1
    There's no point in using map if you're not returning anything. When you switch to .keys() then also switch to .forEach() – doublejosh Apr 05 '20 at 21:53
  • 5
    `Object.fromEntries` is ES2019, not ES2015. Also, as others have said, I'd **highly** recommend replacing the `.map` in the first snippet with `forEach`, or something else more appropriate than `.map` – CertainPerformance May 26 '20 at 16:53
  • tried a typescript version of the last example: `const objectMap = ( obj: { [key: string]: V }, fn: (value: V, key: string, index: number) => R ) => Object.fromEntries(Object.entries(obj).map(([k, v], i) => [k, fn(v, k, i)])); ` – Heechul Ryu Feb 16 '23 at 08:03
573

How about a one-liner in JS ES10 / ES2019 ?

Making use of Object.entries() and Object.fromEntries():

let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]));

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

This function uses recursion to square nested objects as well:

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

With ES7 / ES2016 you can't use Objects.fromEntries, but you can achieve the same 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})));

ES6 / ES2015 Doesn't allow Object.entries, but you could use Object.keys instead:

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

ES6 also introduced for...of loops, which allow a more imperative style:

let newObj = {}

for (let [k, v] of Object.entries(obj)) {
  newObj[k] = v * v;
}


array.reduce()

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


Inherited properties and the prototype chain:

In some rare situation you may need to map a class-like object which holds properties of an inherited object on its prototype-chain. In such cases Object.keys() and Object.entries() won't work, because these functions do not include the prototype chain.

If you need to map inherited properties, you can use for (key in myObj) {...}.

Here is an example of such situation:

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

However, please do me a favor and avoid inheritance. :-)

Chad Moran
  • 12,834
  • 2
  • 50
  • 72
Rotareti
  • 49,483
  • 23
  • 112
  • 108
  • 1
    Beautiful but I got caught by the fact that `Object.keys` doesn't enumerate inherited properties. I suggest you add a warning. – David Braun Mar 13 '17 at 21:26
  • To shorten your answer, just use `Object.entries({a: 1, b: 2, c: 3})`. –  Oct 03 '17 at 07:50
  • 1
    You have some typos (you refer to `(o, f)` as arguments but use `obj` in the body. – kzahel Oct 11 '17 at 17:20
  • Hey, can you provide more information about why we should avoid inheritance? Some good article would be enough :) – Jakub Małojło Dec 04 '17 at 07:05
  • `Object.entries({a: 1, b: 2}).map(([k, v]) => \`hello ${k} ${v}\`)` did the trick for me. Thank you! – Doug Jan 30 '18 at 08:47
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries – Mahdi Bashirpour Jun 11 '18 at 13:31
  • @DavidBraun If your using classes/inherited properties, or getter/setters, then you should probably introduce a `copyFrom` function to those classes. There's no way to reliably clone anything but JSON parsable objects i.e. basic objects. That's been my experience after looking into it quite a bit (this goes for Lodash, Ramda clone as well) – Drenai Feb 01 '19 at 09:14
  • 1
    The solution `Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * v})))` does not work if `obj` is an empty object. Change to: `Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v})))`. – blackcatweb Jul 10 '19 at 16:29
149

No native methods, but lodash#mapValues will do the job brilliantly

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
Mario
  • 3,223
  • 3
  • 19
  • 21
  • 9
    There is no need to add an extra HTTP call and an extra library just for one function. In any case, this answer is now outdated and you can simply call `Object.entries({a: 1, b: 2, c: 3})` to get an array. –  Oct 03 '17 at 07:49
  • 21
    Why is getting an array useful? The requirement was for a mapped object. Also there’s no implied HTTP call in using Lodash, a popular library essentially built for this type of thing, although there’s quite a few things that ES6 has precluded the necessity of using utility function libraries to some degree lately. – corse32 Dec 04 '17 at 13:51
  • 6
    extra HTTP call to pull Lodash, I guess. If this is your only use case, indeed it's an overkill. Nonetheless, it makes sense to have this answer around for Lodash is such a widespread library. – igorsantos07 Mar 29 '18 at 06:21
  • 2
    not to mention you should actually use `_.map()` so you get the key as the second argument - as required by the OP. – igorsantos07 Mar 29 '18 at 06:22
  • 5
    `_.mapValues()` does also provide the key as the 2nd argument. Also `_.map()` would not work here as it only returns arrays, not objects: `_.map({ 'a': 1, 'b': 2, 'c': 3}, n => n * 3) // [3, 6, 9]` – MeltedPenguin Jun 04 '20 at 13:20
61

It's pretty easy 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: this version also allows you to (optionally) set the this context for the callback, just like the Array method.

EDIT - changed to remove use of Object.prototype, to ensure that it doesn't clash with any existing property named map on the object.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • (Not the downvoter) Where did `c` come from? Was that a copy/paste mistake? – JLRishe Jan 27 '15 at 20:56
  • 7
    Ok. :) A comment on [this question](http://stackoverflow.com/questions/28179509/transforming-nested-objects-to-an-array#28179747) brought me here. I'll +1 this since the accepted answer is clearly misusing `map`, but I must say that modifying `Object.prototype` doesn't sit well with me, even if it isn't enumerable. – JLRishe Jan 27 '15 at 21:05
  • 2
    @JLRishe Thanks. IMHO in ES5 there's really no longer any reason *not* to modify `Object.prototype` other than the (theoretical) risk of collision with other methods. FWIW, I can't understand how the other answer got as many votes as it has, it's completely wrong. – Alnitak Jan 27 '15 at 21:07
  • 2
    Yeah, collisions were what I had in mind, especially with a name like `map`. – JLRishe Jan 27 '15 at 21:10
  • @NickManning I've got a downvote roughly contemporary with your comment - if yours, an explanation would be nice. – Alnitak Mar 05 '15 at 23:04
  • @Alnitak wasn't me...I just upvoted your answer in fact – Nick Manning Mar 06 '15 at 02:22
  • Modifying `Object.prototype` can cause issues with jQuery specifically Sizzle – u01jmg3 Nov 01 '15 at 21:16
  • 1
    @u01jmg3 it can, unless done safely as a non-enumerable property as shown in my code above – Alnitak Nov 02 '15 at 00:30
  • 1
    Extending objects you don't own is never advisable, regardless of ES version. One day ES will have `Object.prototype.map` with slightly different semantics. Good luck finding that bug. – fregante Aug 08 '16 at 11:43
  • @bfred.it 1) if that were true it would have happened already - there's been ample opportunity to add it. 2) these semantics are identical to `Array.prototype.map` 3) _existing_ code using the function above wouldn't break, because it would override the hypothetical ES version. – Alnitak Aug 08 '16 at 11:57
  • 3
    This **already** doesn't work: `o = {map: true, image: false}`. You're _risking it_ just to write `o.map(fn)` instead of `map(o,fn)` – fregante Aug 08 '16 at 12:03
  • 2
    @bfred.it and I've updated accordingly. I'd note that this only removes the risk of clashing with a property of that same name. I checked, and there's so few properties on `Object` that I think if ES ever did acquire this method it would be `Object.map` rather than `Object.prototype.map` (and this is also consistent with the other "static" methods on `Object`) – Alnitak Aug 08 '16 at 12:09
27

You could use Object.keys and then forEach over the returned array of keys:

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

Or in a more modular fashion:

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 that Object.keys returns an array containing only the object's own enumerable properties, thus it behaves like a for..in loop with a hasOwnProperty check.

Mattias Buelens
  • 19,609
  • 4
  • 45
  • 51
26

This is really annoying, and everyone in the JS community knows it. There should be this functionality:

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}

here is the naïve implementation:

Object.map = function(obj, fn, ctx){
    
    const ret = {};

    for(let k of Object.keys(obj)){
        ret[k] = fn.call(ctx || null, k, obj[k]);
    });

    return ret;
};

it is super annoying to have to implement this yourself all the time ;)

If you want something a little more sophisticated, that doesn't interfere with the Object class, try this:

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

but it is safe to add this map function to Object, just don't add to Object.prototype.

Object.map = ... // fairly safe
Object.prototype.map ... // not ok
Zachiah
  • 1,750
  • 7
  • 28
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • 1
    Why do you add this function to the global `Object` object? Just have `const mapObject = (obj, fn) => { [...]; return ret; }`. – Amberlamps Oct 14 '16 at 06:44
  • 8
    Because it should be a method on Object :) – Alexander Mills Oct 14 '16 at 08:21
  • 1
    As long as we don't mess with Object.prototype then we should be ok – Alexander Mills Oct 14 '16 at 08:22
  • 1
    If map were just a random vague thing, sure. But `.map` is generally understood as a Functor interface, and there's no single definition of Functor for "Object" because virtually anything in javascript can be an Object, including things with their own very distinct and defined .map interfaces/logic. – Dtipson Jul 12 '17 at 19:12
  • here a mess with arguments passed to map callback – Gleb Dolzikov Dec 08 '17 at 13:20
  • You could do a check to see if Object.getPrototypeOf(obj) === Object.prototype and if that's false, through an error. – Alexander Mills Dec 08 '17 at 18:48
  • are you thinking (v, k) instead of (k,v)? Yes, I can understand your PoV, to me it doesn't matter, as long as it's consistent with other similar APIs. – Alexander Mills Feb 01 '18 at 06:55
  • 1
    In my opinion first argument of callback should be iterated element itself, not a key. For example i assume it should give me same object, as it usualy happen with common map : Object.map({prop:1}, el => el) but it returns keys with value of same key name... – Gleb Dolzikov Feb 01 '18 at 06:55
  • you may prefer the Array.prototype.reduce technique instead of the original one. – Alexander Mills Feb 01 '18 at 06:56
  • also, note that fn.call(null) is opinionated, I think that it needs to be another context besides null. – Alexander Mills Feb 01 '18 at 06:57
  • Why do we need use .call at all? To avoid wrong context? – Gleb Dolzikov Feb 02 '18 at 07:50
  • You need Function.prototype.call or Function.prototype.apply so that you can use the appropriate context, as need be. in this case, it's hardcoded to null, but you may wish to use a different value. In other similar official APIs, the context is an optional 3rd argument to the outer function. – Alexander Mills Feb 02 '18 at 08:53
  • 1
    What's inherently wrong about adding the function to Object.prototype? It could be added as non-enumerable function. Or are you worried about objects that might have `map` as a key, obscuring the `map()` function? – Simon East Oct 31 '21 at 00:39
  • Ever heard of the law of unintended consequences Simon? – Alexander Mills Oct 31 '21 at 04:37
20

Minimal version

ES2017

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

ES2019

Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]))
                                                           ↑↑↑↑↑
Yukulélé
  • 15,644
  • 10
  • 70
  • 94
  • I think this is incorrect because of a typo, I think you mean => `Object.entries(obj).reduce((a, [k, v]) => [a[k] = v * v, a], {})`, angled-brackets instead of parentheses. – Alexander Mills Jan 30 '18 at 18:17
  • @AlexanderMills, my code is correct. Read more about comma operator inside parentheses : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator – Yukulélé Jan 30 '18 at 19:32
  • 1
    basically, my code is equivalent to: `Object.entries(obj).reduce((a, [k, v]) => {a[k] = v * v; return a}, {})` – Yukulélé Jan 30 '18 at 19:44
  • Webstorm warns the developer of usage of "comma expressions" saying that they "are normally the sign of overly clever code" ... lol just sayin' – Alexander Mills Jan 30 '18 at 20:14
  • And he's right. for a more understandable code, use loop: https://stackoverflow.com/a/14810722/806169 – Yukulélé Jan 31 '18 at 09:03
  • 1
    Great answer. `Object.entries().reduce` is the best solution I think with ES6+.Would get more upvotes if it used a `return` statement in the reducer rather than `implicit return` and `comma` operator - which is neat but difficult to read IMHO – Drenai Apr 02 '20 at 22:47
18

I came here looking to find and answer for mapping an object to an array and got this page as a result. In case you came here looking for the same answer I was, here is how you can map and object to an array.

You can use map to return a new array from the object like so:

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});
JoeTron
  • 273
  • 2
  • 4
  • 9
    This doesn't remotely work - it just returns an array of the values of the object, not a new object. I can't believe it has this many up-votes. – Alnitak Aug 08 '16 at 12:10
  • 1
    You are correct, it does not answer the question correctly as they were looking for an answer for mapping an object to and object. I came here looking for an answer for mapping an object to an array and got this page as a result, so I will leave my answer and edit it to reflect that it is an alternate answer for people who may have gotten here as a result of looking for mapping an object to arrays. – JoeTron Aug 09 '16 at 17:10
  • 10
    In ES7 that'll trivially be `Object.values(myObject)` – Alnitak Aug 09 '16 at 17:12
  • const obj = { foo: 'bar', baz: 42 }; console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ] – Mahdi Bashirpour Jun 11 '18 at 13:27
  • This doesn't work, just outputs an array with the object values – SujithaW Nov 16 '21 at 12:56
17

JavaScript just got the new Object.fromEntries method.

Example

function mapObject (obj, fn) {
  return Object.fromEntries(
    Object
      .entries(obj)
      .map(fn)
  )
}

const myObject = { a: 1, b: 2, c: 3 }
const myNewObject = mapObject(myObject, ([key, value]) => ([key, value * value]))
console.log(myNewObject)

Explanation

The code above converts the Object into an nested Array ([[<key>,<value>], ...]) wich you can map over. Object.fromEntries converts the Array back to an Object.

The cool thing about this pattern, is that you can now easily take object keys into account while mapping.

Documentation

Browser Support

Object.fromEntries is currently only supported by these browsers/engines, nevertheless there are polyfills available (e.g @babel/polyfill).

HaNdTriX
  • 28,732
  • 11
  • 78
  • 85
  • 2
    Basically, `Object.fromEntries` is the opposite of `Object.entries`. `Object.entries` converts an object to a list of key-value pairs. `Object.fromEntries` converts a list of key-value pairs to an object. – orad Jul 17 '19 at 18:17
  • Not a bad solution. Clean and simple. Except function params are different to `Array.map()`. My preference would be for the map function params to be `(value, key)`. I know this seems somewhat backwards, however that's the order used for `Array.map()` and `Array.forEach()` so it's more consistent. – Simon East Oct 31 '21 at 00:44
13

The accepted answer has two drawbacks:

  • It misuses Array.prototype.reduce, because reducing means to change the structure of a composite type, which doesn't happen in this case.
  • It is not particularly reusable

An ES6/ES2015 functional approach

Please note that all functions are defined in curried form.

// 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));
  • In line A o is reassigned. Since Javascript passes reference values by sharing, a shallow copy of o is generated. We are now able to mutate o within omap without mutating o in the parent scope.
  • In line B map's return value is ignored, because map performs a mutation of o. Since this side effect remains within omap and isn't visible in the parent scope, it is totally acceptable.

This is not the fastest solution, but a declarative and reusable one. Here is the same implementation as a one-line, succinct but less readable:

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

Addendum - why are objects not iterable by default?

ES2015 specified the iterator and iterable protocols. But objects are still not iterable and thus not mappable. The reason is the mixing of data and program level.

Community
  • 1
  • 1
  • 1
    Looks like a bit over engineered; I count 6 functions for what's essentially a loop. Other solutions are far simpler and the accepted answer is 5 times faster too. – fregante Aug 08 '16 at 11:56
  • 5
    @bfred.it What is the purpose of your comment? If you like micro-optimization you shouldn't use `Array.prototype.reduce` either. All I want to illustrate here is that the accepted answer somehow "abuses" `Array.prototype.reduce` and how a purely functional mapping could be implemented. If you're not interested in functional programming, just ignore my answer. –  Aug 08 '16 at 12:13
  • If you're not interested in performance and simplicity, don't read my comment then. :) A lot of features are "abused" but sometimes they fit the abuse better than other solutions. – fregante Aug 08 '16 at 12:57
  • @naomik: I like `Reflect.ownKeys` too, because it is so honest. But this question is about iterating over the data level of an `Object`. Since `Reflect.ownKeys` iterates over all own properties not just the enumerable ones, I replaced it with `Object.keys`. –  Sep 10 '16 at 17:23
  • 2
    "reducing means to change the structure of a composite type" I don't think that's true. An Array.reduce that produces a new array of the exact same length or even the exact same values is perfectly legitimate. Reduce can be use to re-create the functionality of map and/or filter (or both, as in a complex transducer, for which reduce is the basis). In FP, it's a type of fold, and a fold that ends up with the same type is still a fold. – Dtipson Jul 12 '17 at 18:48
  • 2
    @Dtipson You're absolutely right. A `fold` is more generic than a `map` (functor). However, this was my knowledge from mid-2016. That's half an eternity :D –  Jul 12 '17 at 18:59
  • @Dtipson The real problem is that the OP wants to fold an `Object`, which he shouldn't and that an `Object` is a mix of a dictionary and a record in Javascript. –  Jul 12 '17 at 19:03
  • Note that map does not mutate the array but returns a new one. `const omap = f => o => map(x => o[x] = f(o[x])) (keys(o))` – Thomas Nov 06 '17 at 14:56
10

For maximum performance.

If your object doesn't change often but needs to be iterated on often I suggest using a native Map as a 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);
});

Object.entries already works in Chrome, Edge, Firefox and beta Opera so it's a future-proof feature. It's from ES7 so polyfill it https://github.com/es-shims/Object.entries for IE where it doesn't work.

Pawel
  • 16,093
  • 5
  • 70
  • 73
  • How about using **Destructing**? – Константин Ван Dec 13 '16 at 10:43
  • 1
    @K._ it used to be very slow, but I don't know about performance now. It looks like V8 v 5.6 introduced some optimisations for destructuring but that must be measured http://v8project.blogspot.co.uk/ – Pawel Dec 13 '16 at 10:47
  • Or, less performant es7 but immutable: `const fn = v => v * 2; const newObj = Object.entries(myObject).reduce((acc, [k,v]) => Object.assign({}, acc, {[k]: fn(v)}), {});` – mikebridge Jan 25 '17 at 19:51
9

You can convert an object to array simply by using the following:

You can convert the object values to an array:

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

let valuesArray = Object.values(myObject);

console.log(valuesArray);

You can convert the object keys to an array:

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

let keysArray = Object.keys(myObject);

console.log(keysArray);

Now you can perform normal array operations, including the 'map' function

Danubio Müller
  • 139
  • 1
  • 5
8

you can use map method and forEach on arrays but if you want to use it on Object then you can use it with twist like below:

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

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

Or you can use other loops also like $.each method as below example:

$.each(ob,function (key, value) {
  ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}
Haritsinh Gohil
  • 5,818
  • 48
  • 50
  • Where `$` is jquery? – masterxilo Oct 12 '18 at 16:19
  • Yes you can use both $ and jQuery – Haritsinh Gohil Oct 13 '18 at 12:30
  • 1
    @Ronald i have misunderstood, see my updated answer, thanks for not downvoting but truly making aware to my mistake, thanks for improving community. – Haritsinh Gohil Oct 30 '18 at 12:49
  • @bcoughlan yes it is exactly which i have written in comment too, it is squaring the numbers, what do you want then? – Haritsinh Gohil Dec 06 '18 at 04:55
  • @abalter , ronald has written that when i have not mentioned that i was using jQuery for solving it, but if you can see it now it is mentioned clearly on code blocks, so what is the problem dude? – Haritsinh Gohil Sep 16 '19 at 07:12
  • 1
    @HaritsinhGohil I took it to mean that he was criticizing you for giving a jQuery solution. It's true that there is some impetus these days to move away from jQuery, and of course there is Node, but unless an OP asks specifically for pure JS or Node, I think it's fine to assume someone is working in a web environment and that jQuery is almost certain to be available. – abalter Sep 17 '19 at 13:20
6

The map function does not exist on the Object.prototype however you can emulate it like so

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;
});
whitneyit
  • 1,226
  • 8
  • 17
  • Perfomance issues: `typeof callback === 'function'` absolutely redundant. 1) `map` has no meaning without callback 2) if `callback` isn't function, yours `map` implementation just runs meaningless for-loop. Also, `Object.prototype.hasOwnProperty.call( obj, key )` is a bit overkill. – ankhzet Feb 17 '16 at 08:11
6

EDIT: The canonical way using newer JavaScript features is -

const identity = x =>
  x

const omap = (f = identity, o = {}) =>
  Object.fromEntries(
    Object.entries(o).map(([ k, v ]) =>
      [ k, f(v) ]
    )
  )

Where o is some object and f is your mapping function. Or we could say, given a function from a -> b, and an object with values of type a, produce an object with values of type b. As a pseudo type signature -

// omap : (a -> b, { a }) -> { b }

The original answer was written to demonstrate a powerful combinator, mapReduce which allows us to think of our transformation in a different way

  1. m, the mapping function – gives you a chance to transform the incoming element before…
  2. r, the reducing function – this function combines the accumulator with the result of the mapped element

Intuitively, mapReduce creates a new reducer we can plug directly into Array.prototype.reduce. But more importantly, we can implement our object functor implementation omap plainly by utilizing the object monoid, Object.assign and {}.

const identity = x =>
  x
  
const mapReduce = (m, r) =>
  (a, x) => r (a, m (x))

const omap = (f = identity, o = {}) =>
  Object
    .keys (o)
    .reduce
      ( mapReduce
          ( k => ({ [k]: f (o[k]) })
          , Object.assign
          )
      , {}
      )
          
const square = x =>
  x * x
  
const data =
  { a : 1, b : 2, c : 3 }
  
console .log (omap (square, data))
// { a : 1, b : 4, c : 9 }

Notice the only part of the program we actually had to write is the mapping implementation itself –

k => ({ [k]: f (o[k]) })

Which says, given a known object o and some key k, construct an object and whose computed property k is the result of calling f on the key's value, o[k].

We get a glimpse of mapReduce's sequencing potential if we first abstract oreduce

// oreduce : (string * a -> string * b, b, { a }) -> { b }
const oreduce = (f = identity, r = null, o = {}) =>
  Object
    .keys (o)
    .reduce
      ( mapReduce
          ( k => [ k, o[k] ]
          , f
          )
      , r
      )

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

Everything works the same, but omap can be defined at a higher-level now. Of course the new Object.entries makes this look silly, but the exercise is still important to the learner.

You won't see the full potential of mapReduce here, but I share this answer because it's interesting to see just how many places it can be applied. If you're interested in how it is derived and other ways it could be useful, please see this answer.

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 4
    Let us rather use `Map` and `Map.entries`, OK :D. I'm tired of abusing plain objects as map data types. –  Aug 09 '16 at 06:35
  • 2
    I agree `Map` should be used where/when possible, but it does not totally replace the need to use these procedures on plain JS objects tho :D – Mulan Aug 09 '16 at 07:08
4
  const array = Object.keys(object)
  //returns all keys as array ["key", "key"]

  const array = Object.values(object)
  //returns all values as array ["value", "value"]

  const array = Object.entries(object)
  //returns all entries as array = [["key","value"], ["key","value"]]

Use Map on the result.

  array.map()
KISHORE K J
  • 186
  • 2
  • 4
3

Based on @Amberlamps answer, here's a utility function (as a comment it looked ugly)

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

and the use is:

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}
yonatanmn
  • 1,590
  • 1
  • 15
  • 21
3

I came upon this as a first-item in a Google search trying to learn to do this, and thought I would share for other folsk finding this recently the solution I found, which uses the npm package immutable.

I think its interesting to share because immutable uses the OP's EXACT situation in their own documentation - the following is not my own code but pulled from the current immutable-js documentation:

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 } 

Not that Seq has other properties ("Seq describes a lazy operation, allowing them to efficiently chain use of all the higher-order collection methods (such as map and filter) by not creating intermediate collections") and that some other immutable-js data structures might also do the job quite efficiently.

Anyone using this method will of course have to npm install immutable and might want to read the docs:

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

alex.h
  • 118
  • 5
3

Object Mapper in TypeScript

I like the examples that use Object.fromEntries such as this one, but still, they are not very easy to use. The answers that use Object.keys and then look up the key are actually doing multiple look-ups that may not be necessary.

I wished there was an Object.map function, but we can create our own and call it objectMap with the ability to modify both key and value:

Usage (JavaScript):

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

// keep the key and modify the value
let obj = objectMap(myObject, val => val * 2);
// obj = { a: 2, b: 4, c: 6 }


// modify both key and value
obj = objectMap(myObject,
    val => val * 2 + '',
    key => (key + key).toUpperCase());
// obj = { AA: '2', BB: '4', CC: '6' }

Code (TypeScript):

interface Dictionary<T> {
    [key: string]: T;
}

function objectMap<TValue, TResult>(
    obj: Dictionary<TValue>,
    valSelector: (val: TValue, obj: Dictionary<TValue>) => TResult,
    keySelector?: (key: string, obj: Dictionary<TValue>) => string,
    ctx?: Dictionary<TValue>
) {
    const ret = {} as Dictionary<TResult>;
    for (const key of Object.keys(obj)) {
        const retKey = keySelector
            ? keySelector.call(ctx || null, key, obj)
            : key;
        const retVal = valSelector.call(ctx || null, obj[key], obj);
        ret[retKey] = retVal;
    }
    return ret;
}

If you are not using TypeScript then copy the above code in TypeScript Playground to get the JavaScript code.

Also, the reason I put keySelector after valSelector in the parameter list, is because it is optional.

* Some credit go to alexander-mills' answer.

orad
  • 15,272
  • 23
  • 77
  • 113
2

My response is largely based off the highest rated response here and hopefully everyone understands (have the same explanation on my GitHub, too). This is why his impementation with map works:

Object.keys(images).map((key) => images[key] = 'url(' + '"' + images[key] + '"' +    
')');

The purpose of the function is to take an object and modify the original contents of the object using a method available to all objects (objects and arrays alike) without returning an array. Almost everything within JS is an object, and for that reason elements further down the pipeline of inheritance can potentially technically use those available to those up the line (and the reverse it appears).

The reason that this works is due to the .map functions returning an array REQUIRING that you provide an explicit or implicit RETURN of an array instead of simply modifying an existing object. You essentially trick the program into thinking the object is an array by using Object.keys which will allow you to use the map function with its acting on the values the individual keys are associated with (I actually accidentally returned arrays but fixed it). As long as there isn't a return in the normal sense, there will be no array created with the original object stil intact and modified as programmed.

This particular program takes an object called images and takes the values of its keys and appends url tags for use within another function. Original is this:

var images = { 
snow: 'https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305', 
sunny: 'http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-east-   
Matanzas-city- Cuba-20170131-1080.jpg', 
rain: 'https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg' };

...and modified is this:

var images = { 
snow: url('https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305'),     
sunny: url('http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-   
east-Matanzas-city- Cuba-20170131-1080.jpg'), 
rain: url('https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg') 
};

The object's original structure is left intact allowing for normal property access as long as there isn't a return. Do NOT have it return an array like normal and everything will be fine. The goal is REASSIGNING the original values (images[key]) to what is wanted and not anything else. As far as I know, in order to prevent array output there HAS to be REASSIGNMENT of images[key] and no implicit or explicit request to return an array (variable assignment does this and was glitching back and forth for me).

EDIT:

Going to address his other method regarding new object creation to avoid modifying original object (and reassignment appears to still be necessary in order to avoid accidentally creating an array as output). These functions use arrow syntax and are if you simply want to create a new object for future use.

const mapper = (obj, mapFn) => Object.keys(obj).reduce((result, key) => {
                result[key] = mapFn(obj)[key];
                return result;
            }, {});

var newImages = mapper(images, (value) => value);

The way these functions work is like so:

mapFn takes the function to be added later (in this case (value) => value) and simply returns whatever is stored there as a value for that key (or multiplied by two if you change the return value like he did) in mapFn(obj)[key],

and then redefines the original value associated with the key in result[key] = mapFn(obj)[key]

and returns the operation performed on result (the accumulator located in the brackets initiated at the end of the .reduce function).

All of this is being performed on the chosen object and STILL there CANNOT be an implicit request for a returned array and only works when reassigning values as far as I can tell. This requires some mental gymnastics but reduces the lines of code needed as can be seen above. Output is exactly the same as can be seen below:

{snow: "https://www.trbimg.com/img-5aa059f5/turbine/bs-   
md-weather-20180305", sunny: "http://www.cubaweather.org/images/weather-
photos/l…morning-east-Matanzas-city-Cuba-20170131-1080.jpg", rain: 
"https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg"}

Keep in mind this worked with NON-NUMBERS. You CAN duplicate ANY object by SIMPLY RETURNING THE VALUE in the mapFN function.

2
const mapObject = (targetObject, callbackFn) => {
    if (!targetObject) return targetObject;
    if (Array.isArray(targetObject)){
        return targetObject.map((v)=>mapObject(v, callbackFn))
    }
    return Object.entries(targetObject).reduce((acc,[key, value]) => {
        const res = callbackFn(key, value);
        if (!Array.isArray(res) && typeof res ==='object'){
            return {...acc, [key]: mapObject(res, callbackFn)}
        }
        if (Array.isArray(res)){
            return {...acc, [key]: res.map((v)=>mapObject(v, callbackFn))}
        }
        return {...acc, [key]: res};
    },{})
};
const mapped = mapObject(a,(key,value)=> {
    if (!Array.isArray(value) && key === 'a') return ;
    if (!Array.isArray(value) && key === 'e') return [];
    if (!Array.isArray(value) && key === 'g') return value * value;
    return value;
});
console.log(JSON.stringify(mapped)); 
// {"b":2,"c":[{"d":2,"e":[],"f":[{"g":4}]}]}

This function goes recursively through the object and arrays of objects. Attributes can be deleted if returned undefined

Stan
  • 1,800
  • 1
  • 13
  • 15
2

This is my way to do it and results to be worth in terms of efficiency and readability.

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

const newObject = Object.entries(myObject)
  .map(([key, value]) => `${key}: ${value * value}`)
  .join(', ');

console.log(newObject)
DLS93
  • 33
  • 4
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 20 '23 at 23:43
1

I needed a version that allowed modifying the keys as well (based on @Amberlamps and @yonatanmn answers);

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

Edit: slight change to pass in the starting object {}. Allows it to be [] (if the keys are integers)

Symmetric
  • 4,013
  • 3
  • 27
  • 33
1
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 }
1

If anyone was looking for a simple solution that maps an object to a new object or to an array:

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

This may not work for all objects or all mapping functions, but it works for plain shallow objects and straightforward mapping functions which is all I needed.

tailattention
  • 410
  • 3
  • 8
1

To responds more closely to what precisely the OP asked for, the OP wants an object:

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

to have a map method myObject.map,

similar to Array.prototype.map that would be used as follows:

newObject = myObject.map(function (value, label) {
    return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }

The imho best (measured in terms to "close to what is asked" + "no ES{5,6,7} required needlessly") answer would be:

myObject.map = function mapForObject(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property) && property != "map"){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

The code above avoids intentionally using any language features, only available in recent ECMAScript editions. With the code above the problem can be solved lke this:

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

myObject.map = function mapForObject(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property) && property != "map"){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

newObject = myObject.map(function (value, label) {
  return value * value;
});
console.log("newObject is now",newObject);
alternative test code here

Besides frowned upon by some, it would be a possibility to insert the solution in the prototype chain like this.

Object.prototype.map = function(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property)){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

Something, which when done with careful oversight should not have any ill effects and not impact map method of other objects (i.e. Array's map).

humanityANDpeace
  • 4,350
  • 3
  • 37
  • 63
1

First, convert your HTMLCollection using Object.entries(collection). Then it’s an iterable you can now use the .map method on it.

Object.entries(collection).map(...)

reference https://medium.com/@js_tut/calling-javascript-code-on-multiple-div-elements-without-the-id-attribute-97ff6a50f31

Jimubao
  • 411
  • 6
  • 11
1

I handle only strings to reduce exemptions:

Object.keys(params).map(k => typeof params[k] == "string" ? params[k] = params[k].trim() : null);
Idan
  • 3,604
  • 1
  • 28
  • 33
0

If you're interested in mapping not only values but also keys, I have written Object.map(valueMapper, keyMapper), that behaves this way:

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 }
MattiSG
  • 3,796
  • 1
  • 21
  • 32
0

Hey wrote a little mapper function that might help.

    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];
                }
              }
            }
         }
      }
Zak
  • 9
  • 1
0

A different take on it is to use a custom json stringify function that can also work on deep objects. This might be useful if you intend to post it to the server anyway as 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))
Endless
  • 34,080
  • 13
  • 108
  • 131
0

I needed a function to optionally map not only (nor exclusively) values, but also keys. The original object should not change. The object contained only primitive values also.

function mappedObject(obj, keyMapper, valueMapper) {

    const mapped = {};
    const keys   = Object.keys(obj);
    const mapKey = typeof keyMapper == 'function';
    const mapVal = typeof valueMapper == 'function';

    for (let i = 0; i < keys.length; i++) {
        const key = mapKey ? keyMapper(keys[i]) : keys[i];
        const val = mapVal ? valueMapper(obj[keys[i]]) : obj[keys[i]];
        mapped[key] = val;
    }

    return mapped;
}

Use. Pass a keymapper and a valuemapper function:

const o1 = { x: 1, c: 2 }
mappedObject(o1, k => k + '0', v => v + 1) // {x0: 2, c0: 3}
Emilio Grisolía
  • 1,183
  • 1
  • 9
  • 16
-1

I specifically wanted to use the same function that I was using for arrays for a single object, and wanted to keep it simple. This worked for me:

var mapped = [item].map(myMapFunction).pop();
-1

var myObject = { 'a': 1, 'b': 2, 'c': 3 };
Object.keys(myObject).filter((item) => myObject[item] *= 2)
console.log(myObject)
-1

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

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

console.log(myObject);
// { 'a': 2, 'b': 4, 'c': 6 }
wahyu setiawan
  • 178
  • 2
  • 7
-1

Here is another version that allows the mapping function to declare any number of new properties (keys and values) based on the current key and value. E: Now works with arrays too.

Object.defineProperty(Object.prototype, 'mapEntries', {
  value: function(f,a=Array.isArray(this)?[]:{}) {
    return Object.entries(this).reduce( (o, [k,v]) => 
      Object.assign(o, f(v, Array.isArray(a)?Number(k):k, this)),
      a);
  }
});

const data = { a : 1, b : 2, c : 3 };
const calculate = (v, k) => ({
  [k+'_square']: v*v,
  [k+'_cube']: v*v*v
});
console.log( data.mapEntries( calculate ) );
// {
//  "a_square": 1,  "a_cube": 1,
//  "b_square": 4,  "b_cube": 8,
//  "c_square": 9,  "c_cube": 27
// }

// Demonstration with an array:
const arr = [ 'a', 'b', 'c' ];
const duplicate = (v, i) => ({
  [i*2]: v,
  [i*2+1]: v+v
});
console.log( arr.mapEntries( duplicate ) );
// [ "a", "aa", "b", "bb", "c", "cc" ]
loop
  • 825
  • 6
  • 15
-2

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 new _.transform() to transforms object.

Parth Raval
  • 4,097
  • 3
  • 23
  • 36
-2
settings = {
  message_notification: {
    value: true,
    is_active: true,
    slug: 'message_notification',
    title: 'Message Notification'
  },
  support_notification: {
    value: true,
    is_active: true,
    slug: 'support_notification',
    title: 'Support Notification'
  },
};

let keys = Object.keys(settings);
keys.map(key=> settings[key].value = false )
console.log(settings)
Akın Köker
  • 405
  • 5
  • 7
-2

ES6:

Object.prototype.map = function(mapFunc) {
    return Object.keys(this).map((key, index) => mapFunc(key, this[key], index));
}

ES2015:

Object.prototype.map = function (mapFunc) {
    var _this = this;

    return Object.keys(this).map(function (key, index) {
        return mapFunc(key, _this[key], index);
    });
};

test in node:

> a = {foo: "bar"}
{ foo: 'bar' }
> a.map((k,v,i) => v)
[ 'bar' ]
Malix Ren
  • 1
  • 1
-2

Async, anyone?

Despite of the large number of comments, I didn't find a solution for using an async mapper. Here's mine.

Uses p-map, a trusted (@sindresorhus) and small dependency.

(note that no options are passed to p-map. Refer to the docs if you need to adjust concurrency/error handling).

Typescript:

import pMap from "p-map";

export const objectMapAsync = async <InputType, ResultType>(
  object: { [s: string]: InputType } | ArrayLike<InputType>,
  mapper: (input: InputType, key: string, index: number) => Promise<ResultType>
): Promise<{
  [k: string]: ResultType;
}> => {
  const mappedTuples = await pMap(
    Object.entries(object),
    async ([key, value], index) => {
      const result = await mapper(value, key, index);
      return [key, result];
    }
  );

  return Object.fromEntries(mappedTuples);
};

Plain JS:

import pMap from "p-map";

export const objectMapAsync = async (
  object,
  mapper
) => {
  const mappedTuples = await pMap(
    Object.entries(object),
    async ([key, value], index) => {
      const result = await mapper(value, key, index);
      return [key, result];
    }
  );

  return Object.fromEntries(mappedTuples);
};

};

Usage example:

(highly contrived, no error handling, no types)

// Our object in question.
const ourFavouriteCharacters = {
  me: "luke",
  you: "vader",
  everyone: "chewbacca",
};

// An async function operating on the object's values (in this case, strings)
const fetchCharacter = (charName) =>
  fetch(`https://swapi.dev/api/people?search=${charName}`)
    .then((res) => res.json())
    .then((res) => res.results[0]);

// `objectMapAsync` will return the final mapped object to us
//  (wrapped in a Promise)
objectMapAsync(ourFavouriteCharacters, fetchCharacter).then((res) =>
  console.log(res)
);
panepeter
  • 3,224
  • 1
  • 29
  • 41
  • 3
    I can't speak for your downvoter; but I'd posit that perhaps 'async map-over-object' is a unique-enough problem-space that it deserves its own question/answer, not an *additional* answer that doesn't help the original asker on a well-and-thoroughly-already-answered Question. (So, less "this is bad and your are bad", and more "that's now how we do things on Stack Exchange." ) – ELLIOTTCABLE Sep 03 '21 at 16:04
  • 1
    @ELLIOTTCABLE thank you for taking the time to answer on my comment. I usually only answer questions that I have myself, but where I'm not happy with the given answers. This was such a case. After all, answers should help people landing on a question, not only the poster. I'll take the liberty of leaving the answer as-is for now. ✌ – panepeter Sep 03 '21 at 16:54
  • 1
    Show an example how to use objectMapAsync for newbies? – Mecanik Oct 23 '21 at 13:30
  • @Mecanik added an example, hope it helps – panepeter Oct 26 '21 at 18:25
-3

Use following map function to define myObject.map

o => f=> Object.keys(o).reduce((a,c)=> c=='map' ? a : (a[c]=f(o[c],c),a), {})

let map = o => f=> Object.keys(o).reduce((a,c)=> c=='map' ? a : (a[c]=f(o[c],c),a), {})



// TEST init

myObject = { 'a': 1, 'b': 2, 'c': 3 }
myObject.map = map(myObject);        

// you can do this instead above line but it is not recommended 
// ( you will see `map` key any/all objects)
// Object.prototype.map = map(myObject);

// OP desired interface described in question
newObject = myObject.map(function (value, label) {
    return  value * value;
});

console.log(newObject);
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345