Асинхронный JavaScriptРекурсивные структурыhard

Фильтруем товары

Ваша задача — реализовать фильтрацию товаров в интернет-магазине, вернув список продуктов, подходящих по цене и находящихся на складе.

Функция принимает catalog, minPrice и maxPrice.

Функция должна вернуть промис, который разрешится массивом с отфильтрованными товарами (см. пример ниже). Порядок объектов в массиве значения не имеет.

API каталога асинхронное и ненадежное. Методы объектов работают не мгновенно и часто заканчиваются ошибками. В случае получения ошибки, мы должны пытаться выполнять метод повторно, пока не получим нужный ответ.

Описание API

У категории есть три метода. Каждый принимает только колбэк, который срабатывает, когда функция завершила выполнение. Колбэк принимает два аргумента — ошибку и результат работы функции. Если ошибка равна null, то функция завершилась корректно.

На тайпскрипте описание классов выглядело бы так. Если вы не понимаете эту запись, то в конце условия есть пример реализации этих классов.

interface Category { constructor( name: string, inStock: boolean, children?: (Product | Category)[], ): Category; getName( cb: (error: Error | null, name: string) => void ): void; checkInStock( cb: (error: Error | null, inStock: boolean) => void ): void; getChildren( cb: (error: Error | null, children: (Product | Category)[]) => void ): void; }

У продукта также тоже три метода, устроенных по тому же принципу.

interface Product { constructor( name: string, inStock: boolean, price: number ): Product; getName( cb: (error: Error | null, name: string) => void ): void; checkInStock( cb: (error: Error | null, inStock: boolean) => void ): void; getPrice( cb: (error: Error | null, price: number) => void ): void; }

Пример входных данных

const catalog = new Category("Catalog", true, [ new Category("Electronics", true, [ new Category("Smartphones", true, [ new Product("Smartphone 1", true, 1000), new Product("Smartphone 2", true, 900), new Product("Smartphone 3", false, 900), new Product("Smartphone 4", true, 900), new Product("Smartphone 5", true, 900) ]), new Category("Laptops", true, [ new Product("Laptop 1", false, 1200), new Product("Laptop 2", true, 900), new Product("Laptop 3", true, 1500), new Product("Laptop 4", true, 1600) ]), ]), new Category("Books", true, [ new Category("Fiction", false, [ new Product("Fiction book 1", true, 350), new Product("Fiction book 2", false, 400) ]), new Category("Non-Fiction", true, [ new Product("Non-Fiction book 1", true, 250), new Product("Non-Fiction book 2", true, 300), new Product("Non-Fiction book 3", true, 400) ]), ]), ]); const minPrice = 300, maxPrice = 1500; filterProducts(catalog, minPrice, maxPrice) .then(products => console.log(products));

Что должно вернуться

products === [ { name: "Non-Fiction book 2", price: 300 }, { name: "Non-Fiction book 3", price: 400 }, { name: "Laptop 2", price: 900 }, { name: "Smartphone 2", price: 900 }, { name: "Smartphone 4", price: 900 }, { name: "Smartphone 5", price: 900 }, { name: "Smartphone 1", price: 1000 }, { name: "Laptop 3", price: 1500 } ]

Для локального тестирования

В тестирующей системе классы Category и Product предопределены. Чтобы создать каталог локально, можно реализовать свои версии классов. Например, такие:

function helper(cb, value, ok = true, ms = 10) { setTimeout(() => { if (ok) { cb(null, value); // success } else { cb("error"); // bad luck } }, ms); } class Category { #name; #inStock; #children; constructor(name, inStock, children) { this.#name = name; this.#inStock = inStock; this.#children = children; } getName(cb) { helper(cb, this.#name); } checkInStock(cb) { helper(cb, this.#inStock); } getChildren(cb) { helper(cb, this.#children); } } class Product { #name; #inStock; #price; constructor(name, inStock, price) { this.#name = name; this.#inStock = inStock; this.#price = price; } getName(cb) { helper(cb, this.#name); } checkInStock(cb) { helper(cb, this.#inStock); } getPrice(cb) { helper(cb, this.#price); } }