Массивы - это часть нашей повседневной жизни как программистов. Представим, что вы создаете приложение для электронной торговли и в какой-то момент у вас есть массив, подобный следующему:
const skus = [ { "productId": 1, "colorId": 1, "sku": "11-M" }, { "productId": 1, "colorId": 1, "sku": "11-L" }, { "productId": 1, "colorId": 2, "sku": "12-M" }, { "productId": 1, "colorId": 2, "sku": "12-L" }, { "productId": 2, "colorId": 1, "sku": "21-M" }, { "productId": 2, "colorId": 1, "sku": "21-L" } ];
Учитывая этот массив, вашей новой задачей будет получение всех SKU для определенного productId
. Первое, что придет вам в голову, это использование skus.find(s => productId === s.productId)
, и это, вероятно, будет хорошим решением для некоторых сценариев. Однако, когда ваше приложение обрабатывает большой объем данных, это может привести к проблемам с производительностью.
Лучшим подходом для этого случая является преобразование этого массива в индекс для прямого доступа к SKU вместо итерации по массиву.
Для этого мы можем использовать reduce
:
const productIndex = indexByField(skus, “productId”);
и наш индекс будет:
{ "1": [ { "productId": 1, "colorId": 1, "sku": "11-M" }, { "productId": 1, "colorId": 1, "sku": "11-L" }, { "productId": 1, "colorId": 2, "sku": "12-M" }, { "productId": 1, "colorId": 2, "sku": "12-L" } ], "2": [ { "productId": 2, "colorId": 1, "sku": "21-M" }, { "productId": 2, "colorId": 1, "sku": "21-L" } ] }
Теперь вы можете получить все артикулы для конкретного продукта!
Подождите ... как я могу получить артикулы для определенного цвета? 🤔
Как вы могли заметить, мы проиндексировали наш массив только по productId
. Давайте также проиндексируем наш массив по colorId
.
Принимая во внимание текущий статус индекса, лучше всего проиндексировать его значения с помощью нашей функции indexByField
. Да, в данном случае это сработает, но что, если бы у нас было много уровней индексации? …
Рекурсия приходит на помощь!
Давайте объясним это mapObjectValues
подробно:
value
будет нашим массивом при первом вызове и индексом при следующих рекурсивных вызовах.shouldMap
определит, когда прекратить выполнение рекурсивных вызовов. В нашем случае это будетArray.isArray
.mapFn
преобразует значения наших индексов, какArray.prototype.map
для элементов массива. В нашем случае это будет нашаindexByField
функция.
Собираем все вместе 🧩
Мы готовы определить нашу функцию для индексации массива по нескольким полям. Кроме того, мы расширяем Array.prototype
, чтобы мы могли вызывать его прямо из нашего экземпляра:
const productColorIndex = skus.indexBy(“productId”, "colorId");
и его значение будет:
{ "1": { "1": [ { "productId": 1, "colorId": 1, "sku": "11-M" }, { "productId": 1, "colorId": 1, "sku": "11-L" } ], "2": [ { "productId": 1, "colorId": 2, "sku": "12-M" }, { "productId": 1, "colorId": 2, "sku": "12-L" } ] }, "2": { "1": [ { "productId": 2, "colorId": 1, "sku": "21-M" }, { "productId": 2, "colorId": 1, "sku": "21-L" } ] } }
Теперь мы можем получить артикулы, просто просмотрев наш индекс:
productColorIndex["1"]["2"];
Подводя итог 📝
Использование индексов вместо массивов может быть очень полезным для оптимизации многих вариантов использования в вашем приложении с точки зрения временной сложности, особенно если вы обрабатываете большие объемы данных.
Однако этот выигрыш обходится дорого, создание индекса и его хранение в памяти в некоторых сценариях может быть очень дорогостоящим.
Ресурсы
- Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referencia/Objetos_globales/Array/reduce
- Https://en.wikipedia.org/wiki/Stock-keeping_unit