While map
is a proper solution to select 'columns' from a list of objects, it has a downside. If not explicitly checked whether or not the columns exists, it'll throw an error and (at best) provide you with undefined
.
I'd opt for a reduce
solution, which can simply ignore the property or even set you up with a default value.
function getFields(list, field) {
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// check if the item is actually an object and does contain the field
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would work even if one of the items in the provided list is not an object or does not contain the field.
It can even be made more flexible by negotiating a default value should an item not be an object or not contain the field.
function getFields(list, field, otherwise) {
// reduce the provided list to an array containing either the requested field or the alternative value
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would be the same with map, as the length of the returned array would be the same as the provided array. (In which case a map
is slightly cheaper than a reduce
):
function getFields(list, field, otherwise) {
// map the provided list to an array containing either the requested field or the alternative value
return list.map(function(item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
return typeof item === 'object' && field in item ? item[field] : otherwise;
}, []);
}
jsbin example
And then there is the most flexible solution, one which lets you switch between both behaviours simply by providing an alternative value.
function getFields(list, field, otherwise) {
// determine once whether or not to use the 'otherwise'
var alt = typeof otherwise !== 'undefined';
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
else if (alt) {
carry.push(otherwise);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
As the examples above (hopefully) shed some light on the way this works, lets shorten the function a bit by utilising the Array.concat
function.
function getFields(list, field, otherwise) {
var alt = typeof otherwise !== 'undefined';
return list.reduce(function(carry, item) {
return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
}, []);
}
jsbin example
var foos = objArray.pluck("foo");
.Array.prototype.map
function where it exists_.pluck(objArray, "foo")
, however this is part of the Underscore library. The best way is to use the.map
available in ES6