Из массива объектов извлеките значение свойства как массив
У меня есть массив объектов JavaScript со следующей структурой:
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
Я хочу извлечь массив, содержащий значения ключа 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 ]
Есть ли более элегантный и эффективный метод для достижения?
Обратите внимание на предложенный дубликат, этот вопрос спрашивает, как преобразовать объект в массив, этот вопрос спрашивает, как извлечь одно свойство из массива объектов.
26 ответов
Вот более короткий способ достижения этого:
let 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, я обнаружил, что, как ни крути, простой индексированный 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/ сек
var vals=[];
for(var item of testArray){
vals.push(item.val);
}
Array.prototype.map 19 операций в секунду
var vals = testArray.map(function(a) {return a.val;});
Редактировать: Ops/s обновлено 10/2017. TL;DR - .map() медленный. Но иногда читаемость стоит больше, чем производительность.
Проверьте Лодаша _.pluck()
Функция или Подчеркивание _.pluck()
функция. Оба делают именно то, что вы хотите в одном вызове функции!
var result = _.pluck(objArray, 'foo');
Обновить: _.pluck()
был удален с Lodash v4.0.0, в пользу _.map()
в сочетании с чем-то похожим на ответ Ниета. _.pluck()
все еще доступен в Underscore.
Обновление 2: Как отмечает Марк в комментариях, где-то между Lodash v4 и 4.3, была добавлена новая функция, которая снова предоставляет эту функцию. _.property()
является сокращенной функцией, которая возвращает функцию для получения значения свойства в объекте.
Дополнительно, _.map()
теперь позволяет передавать строку в качестве второго параметра, который передается в _.property()
, В результате следующие две строки эквивалентны приведенному выше примеру кода из предварительной версии 4.
var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));
_.property()
, и поэтому _.map()
Кроме того, вы можете указать строку или массив, разделенные точками, чтобы получить доступ к под-свойствам:
var objArray = [
{
someProperty: { aNumber: 5 }
},
{
someProperty: { aNumber: 2 }
},
{
someProperty: { aNumber: 9 }
}
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));
И то и другое _.map()
вызовы в приведенном выше примере вернутся [5, 2, 9]
,
Если вы немного больше разбираетесь в функциональном программировании, взгляните на Рамду R.pluck()
функция, которая бы выглядела примерно так:
var result = R.pluck('foo')(objArray); // or just R.pluck('foo', objArray)
Пример сбора различных полей из массива объектов
let inputArray = [
{ id: 1, name: "name1", value: "value1" },
{ id: 2, name: "name2", value: "value2" },
];
let ids = inputArray.map( (item) => item.id);
let names = inputArray.map((item) => item.name);
let values = inputArray.map((item) => item.value);
console.log(ids);
console.log(names);
console.log(values);
Результат:
[ 1, 2 ]
[ 'name1', 'name2' ]
[ 'value1', 'value2' ]
Лучше использовать какие-то библиотеки, такие как lodash или underscore для межбраузерной гарантии.
В lodash вы можете получить значения свойства в массиве следующим способом
_.map(objArray,"foo")
и в подчеркивание
_.pluck(objArray,"foo")
оба вернутся [ 1, 3, 5 ]
Метод map() создает новый массив, заполненный результатами вызова предоставленной функции для каждого элемента в вызывающем массиве.
let kvArray = [{key: 1, value: 10},
{key: 2, value: 20},
{key: 3, value: 30}]
let reformattedArray = kvArray.map(obj => {
return obj.value
})
Или
const kvArray = [['key1', 'value1'], ['key2', 'value2']]
// Use the regular Map constructor to transform a 2D key-value Array into a map
const myMap = new Map(kvArray)
myMap.get('key1') // returns "value1"
// Use Array.from() to transform a map into a 2D key-value Array
console.log(Array.from(myMap)) // Will show you exactly the same Array as kvArray
// A succinct way to do the same, using the spread syntax
console.log([...myMap])
// Or use the keys() or values() iterators, and convert them to an array
console.log(Array.from(myMap.keys())) // ["key1", "key2"]
С помощью Array.prototype.map
:
function getFields(input, field) {
return input.map(function(o) {
return o[field];
});
}
См. Ссылку выше для прокладки для браузеров до ES5.
В ES6 вы можете сделать:
const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
Я знаю, что уже дано много ответов, и они охватывают огромный спектр решений.
но я хочу добавить кое-что, чего не нашел во всех приведенных выше ответах.
и, например, я буду использовать следующий тип данных
const array = [
{name:"foo",age:23, skills:["reactjs","nodejs","nextjs"]},
{name:"bar",age:25, skills:["angular","java"]}
]
если вы извлекаете один атрибут из всех индексов массива объектов, тогда можно использовать только функцию карты, на которую уже ответили многие члены сообщества, и для справки я напишу код здесь:
array.map(a => a.skills);
приведенный выше фрагмент кода приведет к получению массива длиной 2, это означает, что он вернет навыки, указанные в обоих индексах,
Итак, если вы хотите получить поле, специфичное для одного поля, из объекта массива, например, навыки только бара, вот пример кода для этого, объединив функции фильтра и карты.
Если вы хотите несколько значений в ES6+, будет работать следующее
objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];
let result = objArray.map(({ foo, baz }) => ({ foo, baz }))
Это работает как {foo, baz}
слева используется деструктор объекта, а справа стрелка эквивалентна {foo: foo, baz: baz}
из-за расширенных объектных литералов ES6.
Приведенный выше ответ хорош для одного свойства, но при выборе нескольких свойств из массива используйте это
var arrayObj=[{Name,'A',Age:20,Email:'a.gmail.com'},{Name,'B',Age:30,Email:'b.gmail.com'},{Name,'C',Age:40,Email:'c.gmail.com'}]
сейчас я выбираю только два поля
var outPutArray=arrayObj.map(( {Name,Email} ) => ({Name,Email}) )
console.log(outPutArray)
Если вы хотите также поддерживать массивоподобные объекты, используйте Array.from (ES2015):
Array.from(arrayLike, x => x.foo);
Преимущество метода Array.prototype.map() в том, что входные данные также могут быть Set:
let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
Если у вас есть вложенные массивы, вы можете заставить его работать следующим образом:
const objArray = [
{ id: 1, items: { foo:4, bar: 2}},
{ id: 2, items: { foo:3, bar: 2}},
{ id: 3, items: { foo:1, bar: 2}}
];
let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
console.log(result)
В то время как 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;
}, []);
}
Поскольку вышеприведенные примеры (надеюсь) проливают некоторый свет на то, как это работает, давайте немного сократим функцию, используя Array.concat
функция.
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 : []));
}, []);
}
В общем, если вы хотите экстраполировать значения объекта, которые находятся внутри массива (как описано в вопросе), то вы можете использовать уменьшение, отображение и деструктуризацию массива.
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 в цикле будет следующим:
for (let i in a) {
let temp = [];
for (let j in a[i]) {
temp.push(a[i][j]);
}
array.push(temp);
}
Произведенный вывод: ["word", "again", "some", "1", "2", "3"]
В ES6, если вы хотите динамически передавать поле в виде строки:
function getFields(array, field) {
return array.map(a => a[field]);
}
let result = getFields(array, 'foo');
Простое извлечение нескольких свойств из массива объектов:
let arrayOfObjects = [
{id:1, name:'one', desc:'something'},
{id:2, name:'two', desc:'something else'}
];
//below will extract just the id and name
let result = arrayOfObjects.map(({id, name}) => ({id, name}));
result
будет [{id:1, name:'one'},{id:2, name:'two'}]
Добавьте или удалите свойства по мере необходимости в функции карты
Это зависит от вашего определения "лучше".
Другие ответы указывают на использование карты, которая является естественной (особенно для парней, привыкших к функциональному стилю) и лаконичной. Я настоятельно рекомендую использовать его (если вы не беспокоитесь о нескольких парнях из IE8). Так что, если "лучше" означает "более краткий", "обслуживаемый", "понятный", тогда да, это намного лучше.
С другой стороны, эта красота не обходится без дополнительных затрат. Я не большой поклонник микровыступов, но я провел небольшой тест здесь. Результат предсказуем, старый уродливый способ кажется быстрее, чем функция карты. Так что, если "лучше" означает "быстрее", то нет, оставайтесь со старой школьной модой.
Опять же, это всего лишь микростенд и никоим образом не выступает против использования map
Это просто мои два цента:).
let objArray2 = [];
objArray.forEach(arr => objArray2.push(arr.foo));
Из массива объектов извлеките значение свойства в виде массива с помощью цикла for.
//input
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
//Code
let output=[];
for(let item of objArray){
output.push(item.foo);
}
// Output
[ 1, 3, 5 ]
Приведенный выше ответ хорош для извлечения одного свойства, что если вы хотите извлечь более одного свойства из массива объектов. Вот решение!! В этом случае мы можем просто использовать _.pick(object, [paths])
_.pick(object, [paths])
Предположим, у objArray есть объекты с тремя свойствами, как показано ниже
objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];
Теперь мы хотим извлечь свойства foo и bar из каждого объекта и сохранить их в отдельном массиве. Сначала мы будем перебирать элементы массива, используя map, а затем применяем к нему метод Lodash Library Standard _.pick().
Теперь мы можем извлечь свойства 'foo' и 'bar'.
var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])})
console.log(newArray);
и результат будет [{foo: 1, bar: 2},{foo: 3, bar: 4},{foo: 5, bar: 6}]
наслаждаться!!!
Я бы улучшил только один из ответов, если вы даже не знаете точное свойство объекта, с которым вы играете, используя ниже:
let result = objArray.map(a => a[Object.getOwnPropertyNames(a)]);
Вот еще одна форма использования
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]});
Деструктурировать и получить определенные атрибуты из массива объекта:
const customerList = dealerUserData?.partyDetails.map(
({ partyId, custAccountId }) => ({
partyId,
custAccountId,
customerId: dealerUserData?._id,
userId: dealerUserData?.authUserID,
}),
);