Implement multi-part cost calculator
Company: Stripe
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's ability to implement robust data-processing and numeric computation logic for a multi-part cost calculator, covering input validation, per-item discounts and tax, sorting semantics, multi-currency conversion, and precise monetary rounding.
Part 1: Basic Order Totals with Validation
Constraints
- 0 <= number of orders <= 100000
- 0 <= total number of items across all orders <= 500000
- SKU and order_id values are strings in well-formed inputs
- A valid qty is an int or integer-valued float in the range [1, 10^12]; bool is invalid
- Unit prices should be finite numeric values; invalid catalog prices are reported as invalid_price items
Examples
Input: ({'A': 10.0, 'B': 2.5}, [{'order_id': 'o2', 'items': [{'sku': 'A', 'qty': 2}, {'sku': 'X', 'qty': 5}]}, {'order_id': 'o1', 'items': [{'sku': 'B', 'qty': 4}, {'sku': 'A', 'qty': 0}, {'sku': 'B', 'qty': '3'}]}])
Expected Output: {'results': [{'order_id': 'o1', 'total_cost': 10.0}, {'order_id': 'o2', 'total_cost': 20.0}], 'errors': {'o2': {'missing_skus': ['X']}, 'o1': {'invalid_items': [{'sku': 'A', 'qty': 0, 'reason': 'invalid_qty'}, {'sku': 'B', 'qty': '3', 'reason': 'invalid_qty'}]}}}
Explanation: o1 totals only 4 * 2.5. o2 totals only 2 * 10.0. Results are sorted by order_id.
Input: ({'A': 1.0}, [])
Expected Output: {'results': [], 'errors': {}}
Explanation: Edge case: no orders produces no results and no errors.
Input: ({'A': 3.0}, [{'order_id': 'solo', 'items': []}])
Expected Output: {'results': [{'order_id': 'solo', 'total_cost': 0.0}], 'errors': {}}
Explanation: An order with no items has total 0.
Input: ({'A': 1.0}, [{'order_id': 'o1', 'items': [{'sku': 'A', 'qty': 1000000000000}, {'sku': 'A', 'qty': 1000000000001}, {'sku': 'A', 'qty': 1.5}, {'sku': 'A', 'qty': True}]}])
Expected Output: {'results': [{'order_id': 'o1', 'total_cost': 1000000000000.0}], 'errors': {'o1': {'invalid_items': [{'sku': 'A', 'qty': 1000000000001, 'reason': 'invalid_qty'}, {'sku': 'A', 'qty': 1.5, 'reason': 'invalid_qty'}, {'sku': 'A', 'qty': True, 'reason': 'invalid_qty'}]}}}
Explanation: The maximum allowed qty is valid. Overflow-like, fractional, and boolean quantities are invalid.
Solution
def solution(price_catalog, orders):
import math
MAX_QTY = 10**12
def valid_qty(q):
if isinstance(q, bool):
return False
if isinstance(q, int):
return 1 <= q <= MAX_QTY
if isinstance(q, float):
return math.isfinite(q) and q.is_integer() and 1 <= q <= MAX_QTY
return False
def valid_price(p):
return isinstance(p, (int, float)) and not isinstance(p, bool) and math.isfinite(p)
if not isinstance(price_catalog, dict):
price_catalog = {}
if not isinstance(orders, list):
return {'results': [], 'errors': {'__global__': {'invalid_orders': True}}}
results = []
errors = {}
for idx, order in enumerate(orders):
if isinstance(order, dict):
order_id = order.get('order_id', f'__missing_order_id_{idx}')
items = order.get('items', [])
else:
order_id = f'__malformed_order_{idx}'
items = []
total = 0.0
missing_skus = []
invalid_items = []
if not isinstance(items, list):
invalid_items.append({'sku': None, 'qty': None, 'reason': 'items_not_list'})
items = []
for item in items:
if not isinstance(item, dict):
invalid_items.append({'sku': None, 'qty': None, 'reason': 'malformed_item'})
continue
sku = item.get('sku')
qty = item.get('qty')
if not valid_qty(qty):
invalid_items.append({'sku': sku, 'qty': qty, 'reason': 'invalid_qty'})
continue
if sku not in price_catalog:
missing_skus.append(sku)
continue
price = price_catalog[sku]
if not valid_price(price):
invalid_items.append({'sku': sku, 'qty': qty, 'reason': 'invalid_price'})
continue
total += int(qty) * float(price)
results.append({'order_id': order_id, 'total_cost': total})
order_errors = {}
if missing_skus:
order_errors['missing_skus'] = missing_skus
if invalid_items:
order_errors['invalid_items'] = invalid_items
if order_errors:
errors[order_id] = order_errors
results.sort(key=lambda row: row['order_id'])
return {'results': results, 'errors': errors}Time complexity: O(N + O log O), where N is the total number of items and O is the number of orders. The O log O term is from sorting results by order_id.. Space complexity: O(O + E), where O is the number of result rows and E is the total number of reported errors. For very large inputs, orders can be processed in batches and externally sorted by order_id..
Hints
- Separate validation from arithmetic so invalid data never reaches the multiplication step.
- Collect per-order errors while scanning items, then sort only the final results list.
Part 2: Order Totals with Discounts, Tax, and Custom Sorting
Constraints
- 0 <= number of orders <= 100000
- 0 <= total number of items across all orders <= 500000
- Valid qty is an int or integer-valued float in the range [1, 10^12]; bool is invalid
- Valid discount_rate and tax_rate values are finite numbers in [0.0, 1.0]
- sort_by must be order_id or total_cost; otherwise order_id is used
Examples
Input: ({'A': 10.0, 'B': 5.0}, [{'order_id': 'o2', 'tax_rate': 0.1, 'items': [{'sku': 'A', 'qty': 2}]}, {'order_id': 'o1', 'items': [{'sku': 'B', 'qty': 4}]}], {'A': 0.2}, 'order_id', False)
Expected Output: {'results': [{'order_id': 'o1', 'total_cost': 20.0}, {'order_id': 'o2', 'total_cost': 17.6}], 'errors': {}}
Explanation: o2 gets a 20 percent discount on A before 10 percent tax. Results are sorted by order_id.
Input: ({'A': 100.0, 'B': 50.0}, [{'order_id': 'o1', 'tax_rate': -0.1, 'items': [{'sku': 'A', 'qty': 1}, {'sku': 'B', 'qty': 2}, {'sku': 'X', 'qty': 1}]}, {'order_id': 'o2', 'tax_rate': 0.2, 'items': [{'sku': 'B', 'qty': '2'}, {'sku': 'A', 'qty': 1}]}], {'A': 1.2, 'B': 0.5}, 'total_cost', True)
Expected Output: {'results': [{'order_id': 'o1', 'total_cost': 150.0}, {'order_id': 'o2', 'total_cost': 120.0}], 'errors': {'o1': {'missing_skus': ['X'], 'invalid_discounts': [{'sku': 'A', 'discount_rate': 1.2}], 'invalid_tax_rate': -0.1}, 'o2': {'invalid_items': [{'sku': 'B', 'qty': '2', 'reason': 'invalid_qty'}], 'invalid_discounts': [{'sku': 'A', 'discount_rate': 1.2}]}}}
Explanation: Invalid discount on A is ignored. Invalid tax on o1 is treated as 0. Results are sorted by total_cost descending.
Input: ({'A': 1.0}, [], None, 'order_id', False)
Expected Output: {'results': [], 'errors': {}}
Explanation: Edge case: empty order list.
Input: ({'A': 10.0, 'B': 20.0}, [{'order_id': 'edge', 'tax_rate': 1.0, 'items': [{'sku': 'A', 'qty': 3}, {'sku': 'B', 'qty': 1}]}], {'A': 1.0, 'B': 0.0}, 'total_cost', False)
Expected Output: {'results': [{'order_id': 'edge', 'total_cost': 40.0}], 'errors': {}}
Explanation: A has a full discount, B has no discount, and 100 percent tax doubles the remaining subtotal.
Input: ({'A': 2.0}, [{'order_id': 'b', 'items': [{'sku': 'A', 'qty': 1}]}, {'order_id': 'a', 'items': [{'sku': 'A', 'qty': 3}]}], {}, 'sku', False)
Expected Output: {'results': [{'order_id': 'a', 'total_cost': 6.0}, {'order_id': 'b', 'total_cost': 2.0}], 'errors': {'__global__': {'invalid_sort_by': 'sku'}}}
Explanation: Invalid sort_by is reported and sorting falls back to order_id ascending.
Solution
def solution(price_catalog, orders, discounts=None, sort_by='order_id', descending=False):
import math
MAX_QTY = 10**12
def valid_qty(q):
if isinstance(q, bool):
return False
if isinstance(q, int):
return 1 <= q <= MAX_QTY
if isinstance(q, float):
return math.isfinite(q) and q.is_integer() and 1 <= q <= MAX_QTY
return False
def valid_number(x):
return isinstance(x, (int, float)) and not isinstance(x, bool) and math.isfinite(x)
def valid_rate(x):
return valid_number(x) and 0.0 <= float(x) <= 1.0
if not isinstance(price_catalog, dict):
price_catalog = {}
if not isinstance(discounts, dict):
discounts = {}
if not isinstance(orders, list):
return {'results': [], 'errors': {'__global__': {'invalid_orders': True}}}
results = []
errors = {}
for idx, order in enumerate(orders):
if isinstance(order, dict):
order_id = order.get('order_id', f'__missing_order_id_{idx}')
items = order.get('items', [])
else:
order_id = f'__malformed_order_{idx}'
items = []
subtotal = 0.0
missing_skus = []
invalid_items = []
invalid_discounts = []
seen_bad_discounts = set()
invalid_tax_marker = None
tax_rate = 0.0
if isinstance(order, dict) and 'tax_rate' in order:
raw_tax = order.get('tax_rate')
if valid_rate(raw_tax):
tax_rate = float(raw_tax)
else:
invalid_tax_marker = raw_tax
if not isinstance(items, list):
invalid_items.append({'sku': None, 'qty': None, 'reason': 'items_not_list'})
items = []
for item in items:
if not isinstance(item, dict):
invalid_items.append({'sku': None, 'qty': None, 'reason': 'malformed_item'})
continue
sku = item.get('sku')
qty = item.get('qty')
if not valid_qty(qty):
invalid_items.append({'sku': sku, 'qty': qty, 'reason': 'invalid_qty'})
continue
if sku not in price_catalog:
missing_skus.append(sku)
continue
price = price_catalog[sku]
if not valid_number(price):
invalid_items.append({'sku': sku, 'qty': qty, 'reason': 'invalid_price'})
continue
discount_rate = 0.0
if sku in discounts:
raw_discount = discounts[sku]
if valid_rate(raw_discount):
discount_rate = float(raw_discount)
else:
if sku not in seen_bad_discounts:
invalid_discounts.append({'sku': sku, 'discount_rate': raw_discount})
seen_bad_discounts.add(sku)
subtotal += int(qty) * float(price) * (1.0 - discount_rate)
total = subtotal * (1.0 + tax_rate)
results.append({'order_id': order_id, 'total_cost': total})
order_errors = {}
if missing_skus:
order_errors['missing_skus'] = missing_skus
if invalid_items:
order_errors['invalid_items'] = invalid_items
if invalid_discounts:
order_errors['invalid_discounts'] = invalid_discounts
if invalid_tax_marker is not None:
order_errors['invalid_tax_rate'] = invalid_tax_marker
if order_errors:
errors[order_id] = order_errors
if sort_by not in ('order_id', 'total_cost'):
errors.setdefault('__global__', {})['invalid_sort_by'] = sort_by
sort_by = 'order_id'
if sort_by == 'order_id':
results.sort(key=lambda row: row['order_id'], reverse=bool(descending))
else:
results.sort(key=lambda row: (row['total_cost'], row['order_id']), reverse=bool(descending))
return {'results': results, 'errors': errors}Time complexity: O(N + O log O), where N is the total number of items and O is the number of orders. Sorting dominates after the item scan.. Space complexity: O(O + E), where O is the number of result rows and E is the number of reported errors. With very large inputs, process orders in streaming batches and use an external merge sort for the selected sort key..
Hints
- Apply discounts before tax, and apply tax once to the order subtotal.
- For deterministic sorting by total_cost, include order_id as a secondary key.
Part 3: Multi-Currency Order Totals with Banker's Rounding
Constraints
- 0 <= number of orders <= 100000
- 0 <= total number of items across all orders <= 500000
- Valid qty is an int or integer-valued float in the range [1, 10^12]; bool is invalid
- Valid discount_rate and tax_rate values are finite numbers in [0.0, 1.0]
- Required currency rates must be finite positive numbers
- Rounded outputs must use ROUND_HALF_EVEN to two decimal places
Examples
Input: ({'A': 10.0, 'B': 5.0}, [{'order_id': 'e1', 'currency': 'EUR', 'items': [{'sku': 'A', 'qty': 1}, {'sku': 'B', 'qty': 2}]}, {'order_id': 'u1', 'currency': 'USD', 'items': [{'sku': 'A', 'qty': 3}]}], {'USD': 1.0, 'EUR': 1.2}, 'USD')
Expected Output: {'results': [{'order_id': 'e1', 'total_cost': 24.0, 'currency': 'USD'}, {'order_id': 'u1', 'total_cost': 30.0, 'currency': 'USD'}], 'errors': {}}
Explanation: e1 totals 20 EUR, converted to 24 USD. u1 is already in USD.
Input: ({'A': 10.0}, [{'order_id': 'd1', 'currency': 'EUR', 'tax_rate': 0.1, 'items': [{'sku': 'A', 'qty': 3}]}], {'USD': 1.0, 'EUR': 1.2}, 'USD', {'A': 0.1})
Expected Output: {'results': [{'order_id': 'd1', 'total_cost': 35.64, 'currency': 'USD'}], 'errors': {}}
Explanation: 3 * 10 with 10 percent discount is 27 EUR. After 10 percent tax it is 29.7 EUR, converted to 35.64 USD.
Input: ({'P': 2.685}, [{'order_id': 'r1', 'currency': 'USD', 'items': [{'sku': 'P', 'qty': 1}]}], {'USD': 1.0}, 'USD')
Expected Output: {'results': [{'order_id': 'r1', 'total_cost': 2.68, 'currency': 'USD'}], 'errors': {}}
Explanation: Banker's rounding sends 2.685 to 2.68 because the hundredths digit 8 is even.
Input: ({'A': 10.0}, [{'order_id': 'e', 'currency': 'EUR', 'items': [{'sku': 'A', 'qty': 1}]}, {'order_id': 'g', 'currency': 'GBP', 'items': [{'sku': 'A', 'qty': 1}]}, {'order_id': 'u', 'currency': 'USD', 'items': [{'sku': 'A', 'qty': 1}]}], {'USD': 1.0, 'EUR': 'bad'}, 'USD')
Expected Output: {'results': [{'order_id': 'e', 'total_cost': None, 'currency': 'USD'}, {'order_id': 'g', 'total_cost': None, 'currency': 'USD'}, {'order_id': 'u', 'total_cost': 10.0, 'currency': 'USD'}], 'errors': {'e': {'invalid_rates': [{'currency': 'EUR', 'rate': 'bad'}]}, 'g': {'missing_rates': ['GBP']}}}
Explanation: EUR has an invalid rate, GBP is missing, and USD needs no conversion.
Input: ({'A': 10.0}, [{'order_id': 'x', 'currency': 'USD', 'tax_rate': 1.5, 'items': [{'sku': 'A', 'qty': 1}, {'sku': 'Z', 'qty': 1}, {'sku': 'A', 'qty': 0}]}], {'USD': 1.0}, 'USD', {'A': -0.2})
Expected Output: {'results': [{'order_id': 'x', 'total_cost': 10.0, 'currency': 'USD'}], 'errors': {'x': {'missing_skus': ['Z'], 'invalid_items': [{'sku': 'A', 'qty': 0, 'reason': 'invalid_qty'}], 'invalid_discounts': [{'sku': 'A', 'discount_rate': -0.2}], 'invalid_tax_rate': 1.5}}}
Explanation: The valid A item is charged without discount or tax because both rates are invalid. Missing SKU and invalid qty are reported.
Solution
def solution(price_catalog, orders, rates, target_currency='USD', discounts=None):
import math
from decimal import Decimal, ROUND_HALF_EVEN
MAX_QTY = 10**12
CENT = Decimal('0.01')
ZERO = Decimal('0')
ONE = Decimal('1')
def to_decimal(value):
if isinstance(value, bool):
return None
if isinstance(value, Decimal):
return value if value.is_finite() else None
if isinstance(value, int):
return Decimal(value)
if isinstance(value, float):
if not math.isfinite(value):
return None
return Decimal(str(value))
return None
def valid_qty(q):
if isinstance(q, bool):
return False
if isinstance(q, int):
return 1 <= q <= MAX_QTY
if isinstance(q, float):
return math.isfinite(q) and q.is_integer() and 1 <= q <= MAX_QTY
return False
def valid_fraction(value):
d = to_decimal(value)
return d is not None and ZERO <= d <= ONE
def positive_rate(value):
d = to_decimal(value)
if d is None or d <= ZERO:
return None
return d
if not isinstance(price_catalog, dict):
price_catalog = {}
if not isinstance(orders, list):
return {'results': [], 'errors': {'__global__': {'invalid_orders': True}}}
if not isinstance(rates, dict):
rates = {}
if not isinstance(discounts, dict):
discounts = {}
if not isinstance(target_currency, str) or not target_currency:
target_currency = 'USD'
results = []
errors = {}
for idx, order in enumerate(orders):
if isinstance(order, dict):
order_id = order.get('order_id', f'__missing_order_id_{idx}')
items = order.get('items', [])
source_currency = order.get('currency', 'USD')
else:
order_id = f'__malformed_order_{idx}'
items = []
source_currency = 'USD'
if not isinstance(source_currency, str) or not source_currency:
source_currency = 'USD'
subtotal = Decimal('0')
missing_skus = []
invalid_items = []
invalid_discounts = []
seen_bad_discounts = set()
invalid_tax_marker = None
tax_rate = Decimal('0')
if isinstance(order, dict) and 'tax_rate' in order:
raw_tax = order.get('tax_rate')
if valid_fraction(raw_tax):
tax_rate = to_decimal(raw_tax)
else:
invalid_tax_marker = raw_tax
if not isinstance(items, list):
invalid_items.append({'sku': None, 'qty': None, 'reason': 'items_not_list'})
items = []
for item in items:
if not isinstance(item, dict):
invalid_items.append({'sku': None, 'qty': None, 'reason': 'malformed_item'})
continue
sku = item.get('sku')
qty = item.get('qty')
if not valid_qty(qty):
invalid_items.append({'sku': sku, 'qty': qty, 'reason': 'invalid_qty'})
continue
if sku not in price_catalog:
missing_skus.append(sku)
continue
price = to_decimal(price_catalog[sku])
if price is None:
invalid_items.append({'sku': sku, 'qty': qty, 'reason': 'invalid_price'})
continue
discount_rate = Decimal('0')
if sku in discounts:
raw_discount = discounts[sku]
if valid_fraction(raw_discount):
discount_rate = to_decimal(raw_discount)
else:
if sku not in seen_bad_discounts:
invalid_discounts.append({'sku': sku, 'discount_rate': raw_discount})
seen_bad_discounts.add(sku)
subtotal += Decimal(int(qty)) * price * (ONE - discount_rate)
local_total = subtotal * (ONE + tax_rate)
missing_rates = []
invalid_rates = []
converted = None
if source_currency == target_currency:
converted = local_total
else:
source_rate = None
target_rate = None
if source_currency not in rates:
missing_rates.append(source_currency)
else:
source_rate = positive_rate(rates[source_currency])
if source_rate is None:
invalid_rates.append({'currency': source_currency, 'rate': rates[source_currency]})
if target_currency not in rates:
missing_rates.append(target_currency)
else:
target_rate = positive_rate(rates[target_currency])
if target_rate is None:
invalid_rates.append({'currency': target_currency, 'rate': rates[target_currency]})
if not missing_rates and not invalid_rates:
converted = local_total * source_rate / target_rate
if converted is None:
total_value = None
else:
total_value = float(converted.quantize(CENT, rounding=ROUND_HALF_EVEN))
results.append({'order_id': order_id, 'total_cost': total_value, 'currency': target_currency})
order_errors = {}
if missing_skus:
order_errors['missing_skus'] = missing_skus
if invalid_items:
order_errors['invalid_items'] = invalid_items
if invalid_discounts:
order_errors['invalid_discounts'] = invalid_discounts
if invalid_tax_marker is not None:
order_errors['invalid_tax_rate'] = invalid_tax_marker
if missing_rates:
order_errors['missing_rates'] = missing_rates
if invalid_rates:
order_errors['invalid_rates'] = invalid_rates
if order_errors:
errors[order_id] = order_errors
results.sort(key=lambda row: row['order_id'])
return {'results': results, 'errors': errors}Time complexity: O(N + O log O), where N is the total number of items and O is the number of orders. Each item is scanned once, and each order performs O(1) currency-rate lookups.. Space complexity: O(O + E), where O is the number of result rows and E is the number of reported errors. For very large data, stream item validation/order aggregation, write intermediate results to disk, and externally sort by order_id..
Hints
- Use decimal arithmetic or carefully specified rounding; binary float round-off can change half-cent cases.
- Compute in this order: validate items, apply discounts, apply tax, convert currency, then round.