9

I've re-implemented my own version of what I need but I suspect this is already included in underscore since it's so simple and so closely related to many other functions. But I can't think of what it should be called.

Basically, what I want is a version of _.pluck that works with objects and returns an object instead of an array (with its associated keys).

So, for instance, if I had an object like this:

elements: {
    steam: {
        temperature: 100,
        color: 'orange',
        state: 'gas'
    },
    water: {
        temperature: 50,
        color: 'blue',
        state: 'liquid'
    },
    ice: {
        temperature: 0,
        color: 'white',
        state: 'solid'
    }
}

I'd want to call _.something(elements, 'temperature')

And have it return

{
    steam: 100,
    water: 50,
    ice: 0
}

Instead of _.pluck(elements, 'temperature') which returns

[100, 50, 0]

What is this transformation called and is it already included in underscore? I've written a quick version myself with jQuery's each loop since I'm more familiar with jQuery than underscore (included below) but would prefer to use one from the library if possible.

$.objPluck = function(obj, key) {
    var ret = {};
    $.each(obj, function(k, value) {
        ret[k] = value[key];
    });
    return ret;
}
Brad Dwyer
  • 6,305
  • 8
  • 48
  • 68
  • There's the `Array.map` [method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) which is basically what you're doing, but with an object – Ian Aug 13 '13 at 20:49
  • 1
    Is there a way to make `Array.map` preserve keys? – Brad Dwyer Aug 13 '13 at 20:50
  • 1
    I'm not sure I understand what you mean. And anyways, I was just referring to your initial concern of `But I can't think of what it should be called`. With arrays, I'd call this "mapping" – Ian Aug 13 '13 at 20:52

6 Answers6

10

There's no method to do exactly this in underscore 1.4.4, but if you want to stay in underscore, you can do

_.object(_.keys(elements), _.pluck(elements, 'temperature'))

Demo at jsfiddle courtesy of bfavaretto


As of underscore 1.8.3, this can be done succinctly with _.mapObject(elements, 'temperature');.

Updated demo

Community
  • 1
  • 1
Evan Davis
  • 35,493
  • 6
  • 50
  • 57
8

Worth adding in here a solution that uses ES6 arrow functions, etc:

Object.keys(elements).map(f=>elements[f].temperature)

Note that this won't work on older javascript engines, but in node.js 0.12 with --harmony it works great, or with node.js 4.x and later as well. I just tested this in Chrome 46 and it works fine.

It has the advantage of not needing any extra libraries such as underscore or lodash, but of course only works on new JS engines.

taxilian
  • 14,229
  • 4
  • 34
  • 73
3

on lodash (similar to underscore) , you can use _.mapValues(elements,'temperature').
it's return {steam: 100, water: 50, ice: 0}

lodash ref : _.mapValues

by the way you can use _.mapKeys to build object hash in one line
_.mapKeys(elements,'color') //{orange: Object, blue: Object, white: Object}

pery mimon
  • 7,713
  • 6
  • 52
  • 57
1

You don't need 3rd party libraries for easy stuff like this

// with a plain old for loop
const temps = {}
for (let key in data["elements"]) {
    dikt[key] = temps["elements"][key]["temperature"]
}
return temps

// with a reduce

Object.keys(data['elements']).reduce((acc, key) => acc[key] = data['elements'][key]['temperature'] , temps)
daino3
  • 4,386
  • 37
  • 48
0

You can use a JavaScript for-in loop to iterate through the keys of the object:

var list = [];

// Iterate through each object key, and pick wanted the values.
for(var name in obj)
{
    if (obj.hasOwnProperty(name) && name === "temperature")
    {
       list.push(obj[name]);
    }
}

The above is a simplified example, but you should be able to easily modify to encapsulate it in a more generic function. Also, you don't really need to use JQuery here - native JavaScript implementation will be faster.

The obj.hasOwnProperty(name) check is recommended by JSLint.

Community
  • 1
  • 1
Zorayr
  • 23,770
  • 8
  • 136
  • 129
0

In newer versions there is actually a built in function which does this.

If you give a string, instead of a function, to mapObject, it will interpret that as a property name and behave as a "pluckObject". Most functions are surprisingly liberal in what they accept.

> _.mapObject(elements,'temperature')
Object {steam: 100, water: 50, ice: 0}

If you want an explicit function for the purpose, you can look at how _.pluck is implemented and write your own analogous version:

_.mixin({
  pluckObject: function(obj, key) {
    return _.mapObject(obj, _.property(key));
  }
});

This is a bit redundant, but it makes your intentions clearer.

Hjulle
  • 2,471
  • 1
  • 22
  • 34