Превратите массив в глубоко вложенный объект с помощью этой простой рекурсивной функции.
Потому что правила рекурсии.
Очень распространенный шаблон при приеме данных из API включает индексацию этих данных в вашем приложении, чтобы вы могли прочитать их без поиска.
Пример
Допустим, у вас есть API, который возвращает список элементов, например.
const ITEMS = [ { title: 'The One', contract: '1', author: 'alice' }, { title: 'It Takes Two', contract: '2', author: 'alice' }, { title: '3x a lady', contract: '1', author: 'beck' }, { title: 'Four on the Floor', contract: '1', author: 'candice' }, { title: "In de'Pipe Going Five by Five", contract: '3', author: 'candice' } ]
В своем приложении вы обнаружите, что вам постоянно нужно искать конкретный item
по его author
и contract
. Наивный подход был бы чем-то вроде
const getItemBy = (author, contract) => ITEMS.find( item => author === item.author && contract === item.contract )
Это нормально, если вы будете искать item
нечасто. Но если вам нужно делать это все время, он начинает работать немного медленнее, постоянно ища ITEMS
.
Вместо этого, что, если бы вы могли написать такую функцию, как
const getItemBy = (author, contract) => ITEMS_INDEX[author][contract]
или более безопасно
const getItemBy = (author, contract ) => ITEMS_INDEX[author] ? ITEMS_INDEX[author][contract] : undefined
В этом случае ITEMS_INDEX
потребуется структура вроде
const ITEMS_INDEX = { alice: { '1': { title: 'The One', contract: '1', author: 'alice' }, '2': { title: 'It Takes Two', contract: '2', author: 'alice' }, }, beck: { '1': { title: '3x a lady', contract: '1', author: 'beck' }, }, candice: { '1': { title: 'Four on the Floor', contract: '1', author: 'candice' }, '3': { title: "In de'Pipe Going Five by Five", contract: '3', author: 'candice' } } }
Создание индекса
Чтобы гибко построить ITEMS_INDEX
из ITEMS
, выexplode
ITEMS
.
const ITEMS_ARRAY = explode(ITEMS, 'author', 'contract')
Один ключ
Взломать массив элементов одним ключом легко.
const explodeOne = (array, key) => array.reduce((acc, item) => ({ ...acc, [item[key]]: item }), {})
Таким образом, explodeOne(array, 'author')
даст вам объект, элементы которого помечены их автором, но в этом примере каждый автор может иметь несколько элементов, различающихся contract
элемента.
Много ключей
Если вам нужно несколько ключей, как в нашем примере выше, вам нужно использовать рекурсию.
const explode = (array, key, ...rest) => array.reduce((acc, item) => ({ ...acc, [item[key]]: rest.length ? { ...acc[item[key]], ...explode([item], ...rest) } : item }), {})
Что происходит?
Здесь используется ряд удобных функций Javascript.
- Остаточный синтаксис для обработки произвольного количества ключей
(array, key, ...rest)
, - Редуктор массива, который просматривает массив и создает наш последний объект,
- Вычисляемые свойства
[item[key]: ...
означает, что вы можете ссылаться на или создавать свойство в объекте, не зная об этом заранее, - Обозначение распространения объекта
{ ...acc, ...explode([item], ...rest) }
, и - Рекурсия, т.е. когда мы снова вызываем
explode
в функцииexplode
для обработки следующего ключа для конкретногоitem
.
Построчное объяснение
const explode = (array, key, ...rest)
определяет функцию explode
как принимающую аргументы array
вместе с key
и, необязательно, массивом других ключей, которые мы будем называть rest
. Если ключей больше нет, то rest = []
.
Эта функция вернет array
, уменьшенное до object
. Массив reducer возвращает постоянно расширяющийся объект acc
(сокращенно от аккумулятора), который на каждом проходе добавляет ...acc
, а именно то, что уже есть в объекте, плюс новый ключ [item[key]]
с некоторыми связанными данными. В нашем примере, если item
- это { title: 'The One', contract: '1', author: 'alice', data: 'one' }
, а key
- «автор», тогда { [item[key]]: ... }
будет { alice: ... }
.
Поскольку Алиса может быть автором нескольких элементов с разными контрактами, если есть больше ключей (например, rest.length !== 0
), нам нужно убедиться, что объект в [item[key]]
включает все, что уже было в acc[item[key]]
ранее, а затем мы хотим explode
только один item
, используя rest
ключей.
Если в rest
больше нет ключей, мы просто вставляем сам item
.
Преимущества
Функция explode
дает вам очень простой способ индексировать данные, предоставленные в формате, не оптимизированном для вашего использования. Многие API-интерфейсы возвращают массивы из key, value
пар, и преобразование их в объект, связанный с помощью предоставленных ключей, может впоследствии сэкономить вам много усилий.
Улучшения: упражнение для читателя
Если у нас уже есть допустимые author
и contract
, тогда все, что нам действительно нужно сохранить в нашем индексе, - это заголовки.
const TITLES_INDEX = { alice: { '1': 'The One', '2': 'It Takes Two' }, beck: { '1': '3x a lady' }, candice: { '1': 'Four on the Floor', '3': "In de'Pipe Going Five by Five" } } const getTitle = (author, contract) => TITLES_INDEX[author] ? TITLES_INDEX[author][contract] : undefined
Что бы вы изменили в приведенной выше функции explode
, чтобы сгенерировать TITLES_INDEX
?
Разместите свой ответ в ответах.
Ссылки
- Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#rest_syntax_parameters
- Https://itnext.io/map-and-reduce-a-primer-9e18a6b7e587
- Https://codeburst.io/computed-property-names-ftw-61000332fff1
- Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_object_literals