Из массива объектов извлеките значение свойства в виде массива
у меня есть массив объектов JavaScript со следующей структурой:
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
Я хочу извлечь массив, содержащий значения key foo
, что приводит к значению [ 1, 3, 5 ]
.
Я сделал это с тривиальным подходом, следующим образом:
function getFields(input, field) {
var output = [];
for (var i=0; i < input.length ; ++i)
output.push(input[i][field]);
return output;
}
var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]
есть ли более элегантный и эффективный способ достичь?
примечание о предложил дублировать, что вопрос спрашивает, Как преобразовать объект до массив, этот вопрос спрашивает, как извлечь одно свойство С массив объектов.
12 ответов:
да, но он опирается на функцию ES5 JavaScript. Это означает, что он не будет работать в IE8 и старше.
var result = objArray.map(function(a) {return a.foo;});
на ES6 совместимых интерпретаторах JS вы можете использовать функции стрелочку для краткости:
var result = objArray.map(a => a.foo);
проверить Лодашь это
_.pluck()
функция или библиотеки Underscore_.pluck()
говоря о решениях JS only, я обнаружил, что, как бы это ни было неэлегантно, простой индексированный
for
цикл является более производительным, чем его альтернативы.https://jsperf.com/extract-prop-from-object-array/
извлечение одного свойства из массива 100000 элементов
традиционный для цикла 368 Ops / sec
var vals=[]; for(var i=0;i<testArray.length;i++){ vals.push(testArray[i].val); }
ES6 для..из цикла 303 Ops / sec
var vals=[]; for(var item of testArray){ vals.push(item.val); }
массив.прототип.карта 19 Ops / sec
var vals = testArray.map(function(a) {return a.val;});
Edit: Ops/s обновлено 10/2017. TL; DR - .карте() работает медленно. Но иногда читаемость стоит больше, чем производительность.
используя
Array.prototype.map
:function getFields(input, field) { return input.map(function(o) { return o[field]; }); }
см. ссылку выше для прокладки для браузеров pre-ES5.
лучше использовать какие-то библиотеки, такие как lodash или underscore для обеспечения кросс-браузера.
в lodash вы можете получить значения свойства в массиве следующим методом
_.map(objArray,"foo")
и в подчеркивание
_.pluck(objArray,"foo")
оба вернутся [ 1, 3, 5]
пока
map
является правильным решением, чтобы выбрать "столбцы" из списка объектов, он имеет обратную сторону. Если явно не проверено, существуют ли столбцы, он выдаст ошибку и (в лучшем случае) предоставит вамundefined
. Я бы выбралreduce
решение, которое может просто игнорировать свойство или даже установить вам значение по умолчанию.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; }, []); }
это будет работать даже если один из элементов списка не является объект или не содержит поля.
его можно даже сделать более гибким путем согласования значения по умолчанию, если элемент не является объектом или не содержит поля.
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; }, []); }
это будет то же самое с картой, так как длина возвращаемого массива будет такой же, как и массив. (В этом случае
map
немного дешевле, чем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; }, []); }
и тогда есть самое гибкое решение, которое позволяет переключаться между обоими поведениями, просто предоставляя альтернативное значение.
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; }, []); }
как примеры выше (надеюсь) пролить свет на то, как это работает, позволяет сократить функцию немного, используя
Это зависит от вашего определения "лучше".
другие ответы указывают на использование карты, что естественно (особенно для парней, привыкших к функциональному стилю) и лаконично. Я настоятельно рекомендую использовать его (если вы не беспокоитесь о нескольких парнях IE8 - IT). Поэтому, если "лучше" означает "более лаконичный", "ремонтопригодный", "понятный", то да, это намного лучше.
С другой стороны, эта красота не приходит без дополнительных затрат. Я не большой поклонник microbench, но я поставил маленький
функция map является хорошим выбором при работе с массивами объектов. Хотя уже было опубликовано несколько хороших ответов, пример использования map с комбинацией с фильтром может быть полезен.
в случае, если вы хотите исключить свойства, значения которых не определены или исключить только определенное свойство, вы можете сделать следующее:
var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined}; var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)}); var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});
Если вы хотите также поддерживают массив-как объекты, использовать массив.от (ES2015):
Array.from(arrayLike, x => x.foo);
преимущество над массив.прототип.map () метод ввода также может быть Set:
let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
ES6
let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }]; let b = a.reduce((acc, x) => [...acc, Object.values(x).map((y, i) => y)], []); console.log(b)
эквивалентное использование for in loop будет следующим:
for (let i in a) { let temp = []; for (let j in a[i]) { temp.push(a[i][j]); } array.push(temp); }
произведен выход: ["слово", "снова", "Некоторые", "1", "2", "3"]