Подсчёт полной стоимости товаров с учётом скидок

Опишите общие price/category через generic-ограничение, чтобы сохранить дополнительные поля товаров. Суммируйте обычные цены и цены после скидки категории; результат примера — 40 и 32.

Короткий ответ

Что ответить на собеседовании

Опишите общие price/category через generic-ограничение, чтобы сохранить дополнительные поля товаров. Суммируйте обычные цены и цены после скидки категории; результат примера — 40 и 32.

Подробный разбор

Ответ с пояснениями

Условие

Дан массив объектов products. Требуется реализовать функцию calculateTotal, принимающую массив товаров и объект скидок discountPercentage. Функция должна вычислить суммарную стоимость всех товаров без применения скидок, а также стоимость с учётом скидок, и вернуть объект result, структура которого продемонстрирована в комментариях к коду. Решение должно быть написано на TypeScript.

const products = [
  { id: 1123, price: 20, name: 'apple', color: 'red', category: 'fruit' },
  { id: 4322, price: 20, name: 'apple', ferm: 'siberia', category: 'vegetable' },
];

function calculateTotal(products, discount) {}

const discountPercentage = { fruit: 10, vegetable: 30 };
const result = calculateTotal(products, discountPercentage);

console.log("receipt", result);
// "receipt", {
//   "totalWithoutDiscount": 40,
//   "totalWithDiscount": 32,
//   "discount": {
//     "fruit": 10,
//     "vegetable": 30
//   },
//   "items": [
//     {
//       "id": 1123,
//       "price": 20,
//       "name": "apple",
//       "color": "red",
//       "category": "fruit"
//     },
//     {
//       "id": 4322,
//       "price": 20,
//       "name": "apple",
//       "ferm": "siberia",
//       "category": "vegetable"
//     }
//   ]
// }

Ответ

Опишите общие price/category через generic-ограничение, чтобы сохранить дополнительные поля товаров. Суммируйте обычные цены и цены после скидки категории; результат примера — 40 и 32.

type Priced = { price: number; category: string };
function calculateTotal<T extends Priced>(products: readonly T[], discount: Record<string, number>) {
  let full = 0, reduced = 0;
  for (const item of products) {
    const rate = Object.hasOwn(discount, item.category) ? discount[item.category] : 0;
    if (!Number.isFinite(item.price) || item.price < 0 ||
        !Number.isFinite(rate) || rate < 0 || rate > 100) throw new RangeError('Некорректные данные');
    const cents = Math.round(item.price * 100);
    full += cents;
    reduced += Math.round(cents * (100 - rate) / 100);
  }
  return { totalWithoutDiscount: full / 100, totalWithDiscount: reduced / 100,
    discount: { ...discount }, items: products.map(item => ({ ...item })) };
}

Товары не мутируют, color и ferm сохраняются. Здесь округление происходит по каждой позиции до копейки; для платёжной системы предпочтительны исходные целые копейки и явно ограниченный диапазон сумм.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее