#javascript #arrays
#javascript #массивы
Вопрос:
Я предполагаю, что это основной вопрос для любого, у кого есть опыт и логический ум, но это поставило меня в тупик на два дня.
Я пытаюсь отфильтровать массив по состояниям, а затем отобразить только одно свойство результирующего массива (ов).
Вот мой массив состояний, который содержит подмножество штатов США:
const states = [{ state: 'AL' }, { state: 'OH' }, { state: 'PA' }]
Вот массив, который я хочу сначала отфильтровать по состоянию, а затем отобразить в новый массив только те значения, которые мне нужны.
refData = [
{
state: 'AL',
details: [
{
code: '1A',
description: 'AL Description 1'
},
{
code: '1B',
description: 'AL Description 2'
},
{
code: '1C',
description: 'AL Description 3'
}
]
},
{
state: 'PA',
details: [
{
code: '1A',
description: 'PA Description 1'
},
{
code: '1B',
description: 'PA Description 2'
}
]
}
]
Вот моя единственная рабочая попытка отфильтровать, а затем отобразить, но это не дает мне того, что мне нужно:
const filteredRefData = refData
.filter((item) => (states.some(stateName => item.state === stateName.state)))
.map((item) => item.details)
Что это дает мне, так это массив ВСЕХ деталей, как кода, так и описания. Что мне нужно, так это ПРОСТО значение описания, но независимо от того, что я пытаюсь, я не могу достичь этого конечного результата.
Что я получаю от этой карты:
[
0: [
0: {code: "1A", description: "AL Description 1"}
1: {code: "1B", description: "AL Description 2"}
2: {code: "1C", description: "AL Description 3"}
],
1: [
0: {code: "1A", description: "PA Description 1"}
1: {code: "1B", description: "PA Description 2"}
]
]
Что мне нужно от этой карты:
[
0: [
0: "AL Description 1"
1: "AL Description 2"
2: "AL Description 3"
],
1: [
0: "PA Description 1"
1: "PA Description 2"
]
]
Я пытался использовать динамические индексы, но это не удалось, потому что любой переданный мной индекс всегда связан с массивом верхнего уровня, а не с вложенным массивом. Я также попробовал метод reduce, но мне было трудно понять, как reduce работает только с парами ключ / значение объекта.
Спасибо за любую помощь, которую вы можете предоставить!
Ответ №1:
Вы были почти на месте. Единственное изменение, которое требовалось, заключалось в том, чтобы в финале .map
не просто возвращать item.details
, а результат map
использования этого массива для извлечения только описания:
const states = [{ state: 'AL' }, { state: 'OH' }, { state: 'PA' }];
const refData = [
{
state: 'AL',
details: [
{
code: '1A',
description: 'AL Description 1'
},
{
code: '1B',
description: 'AL Description 2'
},
{
code: '1C',
description: 'AL Description 3'
}
]
},
{
state: 'PA',
details: [
{
code: '1A',
description: 'PA Description 1'
},
{
code: '1B',
description: 'PA Description 2'
}
]
}
];
const filteredRefData = refData
.filter((item) => (states.some(stateName => item.state === stateName.state)))
.map((item) => item.details.map(({ description }) => description));
console.log(filteredRefData);
Комментарии:
1. Робин, большое тебе спасибо… это сделало это! Я попробовал связать метод map в цепочку несколькими способами, но не таким (правильным) способом. Огромная благодарность!!
Ответ №2:
Вы правы, что это задание для reduce
: map
операция приведет к преобразованию входного массива в соотношении 1: 1 (например, refData.map(state => state.state)
вернет массив той же длины, содержащий только state
атрибут), тогда как reduce
может возвращать массив с большим или меньшим количеством результатов.
В вашем случае вы начинаете с x
количества состояний, но вы будете возвращать y
описания, поэтому map
это не вариант, вы должны использовать reduce
(и это хороший пример того, как использовать функциональный подход).
Например, если вы хотите удалить только аббревиатуру состояния, используя reduce
вместо map
, вы могли бы сделать это, накапливая аббревиатуру состояния:
data.reduce(function(acc, x) {
return acc.concat(x.state);
}, []);
Здесь мы устанавливаем начальное значение накопителя в пустой массив []
(2-й аргумент) и используем concat
для добавления отдельных значений в конец acc
массива — результат тот же, что и при использовании map
.
В вашем случае вы хотите объединить массив описаний (или какой-либо другой атрибут переменной), поэтому вот как вы могли бы сократить описания:
var refData = [
{
state: 'AL',
details: [
{
code: '1A',
description: 'AL Description 1'
},
{
code: '1B',
description: 'AL Description 2'
},
{
code: '1C',
description: 'AL Description 3'
}
]
},
{
state: 'PA',
details: [
{
code: '1A',
description: 'PA Description 1'
},
{
code: '1B',
description: 'PA Description 2'
}
]
}
];
refData.reduce(function(acc, x) {
var descriptions = x.details.map(function(detail) {
return detail.description;
});
return acc.concat(descriptions);
}, []);
или более кратко как
refData.reduce(function(acc, x) {
return acc.concat(x.details.map(detail => detail.description));
}, []);
Комментарии:
1. Отлично! Спасибо… Я попробую это, тем более что я использую это значение для отображения пунктов меню, которые сами по себе являются частью Formik FieldArray. Итак, для кого-то вроде меня нужно продумать множество слоев (скорее для фона дизайна, чем для разработки …)
Ответ №3:
Если вы настаиваете на использовании только filter
и map
, попробуйте это:
const filteredRefData = refData
.filter((item) => (states.some(stateName => item.state === stateName.state)))
.map((item) => item.details.map(d => d.description))
Но это не совсем оптимальное решение. Может быть, попробуйте использовать reduce
вместо этого и сравнить результаты.
Комментарии:
1. Спасибо! Я тоже попробую это.
Ответ №4:
Я предполагаю, что использование поля «описание» решит вашу проблему. Попробуйте это:
const filteredRefData = refData
.filter((item) => (states.some(stateName => item.state === stateName.state)))
.map((item) => item.details.description)
Комментарии:
1. Спасибо… Я пробовал это, но поскольку details сам по себе является массивом, для доступа к свойствам его объекта требуется индекс. Добавление описания возвращает значение undefined.