Iterate over object attributes and modify them

2019-04-03 07:18发布

问题:

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

回答1:

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.



回答2:

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


回答3:

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


回答4:

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