Из массива объектов извлеките значение свойства в виде массива


у меня есть массив объектов 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 501

12 ответов:

вот более короткий способ достижения ее

var result = objArray.map(a => a.foo);

да, но он опирается на функцию ES5 JavaScript. Это означает, что он не будет работать в IE8 и старше.

var result = objArray.map(function(a) {return a.foo;});

на ES6 совместимых интерпретаторах JS вы можете использовать функции стрелочку для краткости:

var result = objArray.map(a => a.foo);

документация

говоря о решениях 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;
    }, []);
}

пример jsbin

это будет работать даже если один из элементов списка не является объект или не содержит поля.

его можно даже сделать более гибким путем согласования значения по умолчанию, если элемент не является объектом или не содержит поля.

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

это будет то же самое с картой, так как длина возвращаемого массива будет такой же, как и массив. (В этом случае 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;
    }, []);
}

jsbin пример

и тогда есть самое гибкое решение, которое позволяет переключаться между обоими поведениями, просто предоставляя альтернативное значение.

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

как примеры выше (надеюсь) пролить свет на то, как это работает, позволяет сократить функцию немного, используя

Это зависит от вашего определения "лучше".

другие ответы указывают на использование карты, что естественно (особенно для парней, привыкших к функциональному стилю) и лаконично. Я настоятельно рекомендую использовать его (если вы не беспокоитесь о нескольких парнях 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]});

https://jsfiddle.net/ohea7mgk/

Если вы хотите также поддерживают массив-как объекты, использовать массив.от (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"]

в ES6, вы можете сделать:

const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)