Underscore.js provides _.each
and _.map
on collections, which is nice, but I need to iterate over all attributes of my object. I need to modify the values and preserve the keys. E.g. I've got something like: {a:1, b:2, c:3}
and I need to perform an operation that changes the value but keeps the keys. Let's say, I'll calculate squares, I should get {a:1, b:4, c:9}
. The question is: how to do that using underscore (not interested in vanilla javascript)? I'd love a method like:
var a = {a:1, b:2, c:3}
_.magic(a, function(item){ return item*item; });
Additionally, it would be great if this was possible to chain it, since I'm doing a map, dump result to perform each and then use a map again (because I need to).
I looked a little bit more into some of my snippets to see if there was an option to mutate the original object, but I didn't find anything interesting, so I'd go with this :\
var original = {a:1, b:2, c:3};
var squaredValues = _.object(_.map(original, function (value, key) {
return [key, value * value];
}));
I'm hoping there's a more elegant solution though.
_.mapObject
(added in version 1.8) is exactly what you're looking for.
For previous versions, can mutate the original object using the third argument to the _.each
callback.
_.each({a:1, b:2, c:3}, function(value, key, obj) { obj[key] = value * value; });
Using _.reduce
with an initial memo can create a brand new object as well.
_.reduce({a:1, b:2, c:3}, function(memo, value, key) {
memo[key] = value * value;
return memo;
}, {});
I'd prefer the _.reduce
approach. A simple mixin will make it behave exactly like _.map
.
_.mixin({
magic: function(obj, iterator, context) {
return _.reduce(obj, function(memo, value, key) {
memo[key] = iterator.call(context, value, key, obj);
return memo;
}, {});
}
});
As Justin said _.mapObject might be what you were looking for. But this creates a new object (extending {}
) which might not be what you want (e.g. if it's not a simple object like new THREE.Vector3()
), Here it's no good to replace the original object with something different.
For that case (modifying values in place in the given object) just use _.each with the third parameter of your iteratee function:
_.each(complexObject, function(value, key, obj) {
obj[key] = value * value;
});
Big thanks to Javier92 who pointed me to the right solution.
Anyway, I don't like to use an underscore method that uses another two underscore methods (it's not readable that much), so I came up with a simple wrapper function that is mixed in into underscore:
// mixin to make our new function available via _
_.mixin({
updateAttributes: function(object, iteratee) {
return _.object(_.map(object, function (value, key) {
return [key, iteratee(value)];
}));
}
});
use it somewhere in your code:
_.updateAttributes({a:1, b:2, c:3}, function(item){ return item*item; })
// Object {a: 1, b: 4, c: 9}
couldn't think of a one-word function (probably there's something better than updateAttributes
).