155

The map function in underscore.js, if called with a javascript object, returns an array of values mapped from the object's values.

_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; }); => [3, 6, 9] 

is there a way to make it preserve the keys? ie, I want a function that returns

{one: 3, two: 6, three: 9} 
1
  • If you, like me, came here looking for a 'mapValues' like function that changes the actual object instead of returning a new one, check this simple solution out: stackoverflow.com/questions/30894044/… Commented Jun 17, 2015 at 14:14

12 Answers 12

261

With Underscore

Underscore provides a function _.mapObject to map the values and preserve the keys.

_.mapObject({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; }); // => { one: 3, two: 6, three: 9 } 

DEMO


With Lodash

Lodash provides a function _.mapValues to map the values and preserve the keys.

_.mapValues({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; }); // => { one: 3, two: 6, three: 9 } 

DEMO

Sign up to request clarification or add additional context in comments.

3 Comments

There have been efforts to get a _.mapValues function into underscore: Relevant Issue, Pull Request for _.mapValues. I hope this goes through :)
it looks to me like you're making an OBJECT into an object, or am i out of my mind?
@jsh In this case, _.map() is returning [['one', 3], ['two', 6], ['three', 9]], which is an array of arrays, and _.object() is turning it back into an object.
59

I managed to find the required function in lodash, a utility library similar to underscore.

http://lodash.com/docs#mapValues

_.mapValues(object, [callback=identity], [thisArg]) 

Creates an object with the same keys as object and values generated by running each own enumerable property of object through the callback. The callback is bound to thisArg and invoked with three arguments; (value, key, object).

Comments

20

How about this version in plain JS (ES6 / ES2015)?

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

jsbin

If you want to map over an object recursively (map nested obj), it can be done like this:

const mapObjRecursive = (obj) => { Object.keys(obj).forEach(key => { if (typeof obj[key] === 'object') obj[key] = mapObjRecursive(obj[key]); else obj[key] = obj[key] * 3; }); return obj; }; 

jsbin

Since ES7 / ES2016 you can use Object.entries instead of Object.keys like this:

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

And since ES2019 you can use Object.fromEntries.

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

Comments

19

var mapped = _.reduce({ one: 1, two: 2, three: 3 }, function(obj, val, key) { obj[key] = val*3; return obj; }, {}); console.log(mapped);
<script src="http://underscorejs.org/underscore-min.js"></script> <script src="https://getfirebug.com/firebug-lite-debug.js"></script>

Comments

13

I know this is old, but now Underscore has a new map for objects :

_.mapObject(object, iteratee, [context]) 

You can of course build a flexible map for both arrays and objects

_.fmap = function(arrayOrObject, fn, context){ if(this.isArray(arrayOrObject)) return _.map(arrayOrObject, fn, context); else return _.mapObject(arrayOrObject, fn, context); } 

1 Comment

Note to lodash users: jdalton has decided to break compatibility with underscore.js over this change. lodash will not support mapObject; look at lodash's mapValues method instead.
4

I know it's been a long time, but still the most obvious solution via fold (aka reduce in js) is missing, for the sake of completeness i'll leave it here:

function mapO(f, o) { return Object.keys(o).reduce((acc, key) => { acc[key] = f(o[key]) return acc }, {}) } 

1 Comment

I am okay with the use of Lodash library but programmers are using these kind of libraries, and not knowing how this things could be achieved in vanilla JS. So, I appreciate your answer!
3

_.map returns an Array, not an Object.

If you want an object you're better off using a different function, like each; if you really want to use map you could do something like this:

Object.keys(object).map(function(value, index) { object[value] *= 3; }) 

but that is confusing, when seeing map one would expect to have an array as result and then make something with it.

2 Comments

I had a feeling reading the docs that it wouldn't be natural to try this in underscore.js. I think my use case is quite natural, why don't they support it?
One reason could likely be that map is used to change an input producing an array as output, you could compose _.object and _.map as @GG. wrote, but that's a matter of taste at this point.
3

I think you want a mapValues function (to map a function over the values of an object), which is easy enough to implement yourself:

mapValues = function(obj, f) { var k, result, v; result = {}; for (k in obj) { v = obj[k]; result[k] = f(v); } return result; }; 

Comments

2
const mapObject = (obj = {}, mapper) => Object.entries(obj).reduce( (acc, [key, val]) => ({ ...acc, [key]: mapper(val) }), {}, ); 

1 Comment

I was about to add this answer myself. Glad I scrolled!
1

_.map using lodash like loop to achieve this

 var result={}; _.map({one: 1, two: 2, three: 3}, function(num, key){ result[key]=num * 3; }); console.log(result) //output {one: 1, two: 2, three: 3} 

Reduce is clever looks like above answare

_.reduce({one: 1, two: 2, three: 3}, function(result, num, key) { result[key]=num * 3 return result; }, {}); //output {one: 1, two: 2, three: 3} 

Comments

0

A mix fix for the underscore map bug :P

_.mixin({ mapobj : function( obj, iteratee, context ) { if (obj == null) return []; iteratee = _.iteratee(iteratee, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, results = {}, currentKey; for (var index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; results[currentKey] = iteratee(obj[currentKey], currentKey, obj); } if ( _.isObject( obj ) ) { return _.object( results ) ; } return results; } }); 

A simple workaround that keeps the right key and return as object It is still used the same way as i guest you could used this function to override the bugy _.map function

or simply as me used it as a mixin

_.mapobj ( options , function( val, key, list ) 

Comments

0

You can use _.mapValues(users, function(o) { return o.age; }); in Lodash and _.mapObject({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; }); in Underscore.

Check out the cross-documentation here: http://jonathanpchen.com/underdash-api/#mapvalues-object-iteratee-identity

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.