Spot the bug - #174: Cart Coupon Evaluator

Why is this checkout coupon always triggering the default tier?

function resolveDiscountTier(subtotal, promoCode) {
  let tier = 'STANDARD';
  const isVip = promoCode === 'VIP_HERO';

  switch (subtotal) {
    case subtotal >= 100 && isVip:
      tier = 'PLATINUM';
      break;
    case subtotal >= 100:
      tier = 'GOLD';
      break;
    case subtotal >= 50:
      tier = 'SILVER';
      break;
    default:
      tier = 'BRONZE';
  }
  return tier;
}

Reply with what is broken and how you would fix it.

The switch statement in JavaScript compares the value passed to it with the values in each case. Here, you’re passing subtotal to switch, but your case statements are evaluating to booleans (true or false). A number will never strictly equal a boolean. The typical way to use switch for ranges is to pass true to the switch statement itself, then have your case conditions evaluate to true when they match.

function resolveDiscountTier(subtotal, promoCode) {
  let tier = 'BRONZE';
  const isVip = promoCode === 'VIP_HERO';

  switch (true) { // Compare true against the truthiness of the cases
    case subtotal >= 100 && isVip:
      tier = 'PLATINUM';
      break;
    case subtotal >= 100:
      tier = 'GOLD';
      break;
    case subtotal >= 50:
      tier = 'SILVER';
      break;
    default:
      tier = 'BRONZE';
  }
  return tier;
}

Spot the Bug answer: The switch statement is comparing the ‘subtotal’ variable directly against boolean expressions, which will always result in no match.

The fix:

Change the switch statement to 'switch (true)' to evaluate the case conditions as booleans.

Why:
In JavaScript, a switch statement compares the value of its expression with the value of each case clause. Here, ‘subtotal’ (a number) is being compared to boolean results of expressions like ‘subtotal >= 100 && isVip’. Since a number will never strictly equal a boolean, none of the cases will match, causing the ‘default’ case to always execute.

First-answer leaderboard

  1. @emmawalter5 - 12 (firsts) :trophy:
  2. @kirupa - 8 (firsts)
  3. @Apexcodes - 6 (firsts)
  4. @adnanahmed - 2 (firsts)

The problem is that switch (subtotal) compares subtotal against each case value. The expressions like subtotal >= 100 && isVip evaluate to booleans, so they won’t match the numeric subtotal.

A simple fix is to use if/else instead:

function resolveDiscountTier(subtotal, promoCode) {
  const isVip = promoCode === 'VIP_HERO';

  if (subtotal >= 100 && isVip) return 'PLATINUM';
  if (subtotal >= 100) return 'GOLD';
  if (subtotal >= 50) return 'SILVER';
  return 'BRONZE';
}

This checks the conditions in order, so VIP customers with a subtotal of at least 100 get PLATINUM, while the other tiers work as expected.

yo @Apexcodes, nice catch on that switch statement. that’s a classic one. we’ll post the official solution later today!

Nice one, @Apexcodes — the if/else version is the cleaner fix here. The part I liked most was the ordering: checking the VIP >= 100 case first, so a VIP customer doesn’t get swallowed by the plain >= 100 check and land on GOLD by mistake. Honestly easier to read at a glance than switch(true) too, even if that one works.