Отображение JavaScript и фильтрация во вложенных массивах

#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.