Account Balance Manager
Role: Software Engineer
Problem Overview
You are building an account balance management system for a payment platform. The system processes financial transactions across multiple accounts and needs to handle various business rules around balance validation and fund coverage.
The problem is divided into three progressive parts:
-
Balance Aggregation: Process transactions and return final account balances
-
Transaction Validation: Reject transactions that would cause negative balances
-
Platform Account Coverage: Use a designated platform account to cover insufficient funds
Input Format
You will receive a list of transactions, where each transaction contains:
-
account_id: Unique identifier for the account -
amount: Transaction amount (positive for deposits, negative for withdrawals)
transactions = [
{"account_id": "account_A", "amount": 100},
{"account_id": "account_B", "amount": 50},
{"account_id": "account_A", "amount": -30},
{"account_id": "account_B", "amount": -80}
]Part 1: Balance Aggregation
Problem Statement
Implement a function get_account_balances(transactions) that processes all transactions and returns the final balance for each account that has a positive balance.
Example
Input:
transactions = [
{"account_id": "account_A", "amount": 100},
{"account_id": "account_B", "amount": 50},
{"account_id": "account_A", "amount": -30},
{"account_id": "account_C", "amount": 200},
{"account_id": "account_B", "amount": -50}
]Output:
get_account_balances(transactions)
# Returns: {"account_A": 70, "account_C": 200}
#
# Calculation:
# account_A: 100 - 30 = 70 (positive, included)
# account_B: 50 - 50 = 0 (zero, excluded)
# account_C: 200 (positive, included)Requirements
-
Process transactions in order
-
Aggregate amounts by account ID
-
Return only accounts with balance > 0
-
Return as a dictionary mapping account_id to balance
Part 2: Transaction Validation
Problem Statement
Extend your solution to validate each transaction before processing. A transaction should be rejected if it would cause the account balance to become negative. Return both the final balances and the list of rejected transactions.
Implement process_transactions(transactions) that returns a tuple of (balances, rejected_transactions).
Example
Input:
transactions = [
{"account_id": "account_A", "amount": 100},
{"account_id": "account_A", "amount": -150}, # Would make balance -50
{"account_id": "account_B", "amount": 50},
{"account_id": "account_A", "amount": -80}, # Valid: 100 - 80 = 20
{"account_id": "account_B", "amount": -100} # Would make balance -50
]Output:
process_transactions(transactions)
# Returns: (
# {"account_A": 20, "account_B": 50},
# [
# {"account_id": "account_A", "amount": -150},
# {"account_id": "account_B", "amount": -100}
# ]
# )
#
# Processing:
# 1. account_A +100 → balance: 100 ✓
# 2. account_A -150 → would be -50 ✗ REJECTED
# 3. account_B +50 → balance: 50 ✓
# 4. account_A -80 → balance: 20 ✓
# 5. account_B -100 → would be -50 ✗ REJECTEDRequirements
-
Process transactions in order
-
Reject any transaction that would make the account balance negative
-
Deposits (positive amounts) are always accepted
-
Return rejected transactions in the order they were encountered
-
Include accounts with zero balance in the result
Part 3: Platform Account Coverage
Problem Statement
The platform can now provide financial assistance. When a transaction would cause a negative balance, instead of rejecting it, the system should transfer funds from a designated platform account to cover the shortfall.
Implement process_with_coverage(transactions, platform_account_id) that returns the total amount of funds transferred from the platform account to cover all shortfalls.
Example
Input:
transactions = [
{"account_id": "platform", "amount": 1000},
{"account_id": "account_A", "amount": 100},
{"account_id": "account_A", "amount": -150}, # Needs 50 coverage
{"account_id": "account_B", "amount": 50},
{"account_id": "account_B", "amount": -100}, # Needs 50 coverage
{"account_id": "account_A", "amount": -30} # Needs 30 coverage
]
platform_account_id = "platform"Output:
process_with_coverage(transactions, "platform")
# Returns: 130
#
# Processing:
# 1. platform +1000 → platform balance: 1000
# 2. account_A +100 → account_A balance: 100
# 3. account_A -150 → would be -50, cover 50 from platform
# account_A balance: 0, platform balance: 950, total_coverage: 50
# 4. account_B +50 → account_B balance: 50
# 5. account_B -100 → would be -50, cover 50 from platform
# account_B balance: 0, platform balance: 900, total_coverage: 100
# 6. account_A -30 → would be -30, cover 30 from platform
# account_A balance: 0, platform balance: 870, total_coverage: 130Requirements
-
Platform account is specified by an additional parameter
-
When a transaction would cause negative balance, transfer exactly enough from the platform account to bring balance to zero
-
The transaction should still be processed (balance ends at 0)
-
Return the total amount of coverage provided
-
Assume platform account always has sufficient funds
Solution Approach
Part 1: Balance Aggregation Solution
Strategy:
-
Use a dictionary to track balance for each account
-
Iterate through transactions, updating balances
-
Filter and return accounts with positive balances
Time Complexity: O(n) where n is the number of transactions
Space Complexity: O(k) where k is the number of unique accounts
Example Implementation:
def get_account_balances(transactions):
balances = {}
for txn in transactions:
account_id = txn["account_id"]
amount = txn["amount"]
balances[account_id] = balances.get(account_id, 0) + amount
# Filter for positive balances only
return {acc: bal for acc, bal in balances.items() if bal > 0}Edge Cases to Consider:
-
Empty transaction list
-
All accounts end with zero or negative balance
-
Single account with multiple transactions
-
Large number of accounts
Part 2: Transaction Validation Solution
Strategy:
-
Track current balance for each account
-
Before processing withdrawal, check if it would cause negative balance
-
If valid, apply transaction; otherwise add to rejected list
-
Return both final balances and rejected transactions
Time Complexity: O(n)
Space Complexity: O(k + r) where r is the number of rejected transactions
Example Implementation:
def process_transactions(transactions):
balances = {}
rejected = []
for txn in transactions:
account_id = txn["account_id"]
amount = txn["amount"]
current_balance = balances.get(account_id, 0)
new_balance = current_balance + amount
if new_balance < 0:
# Reject transaction
rejected.append(txn)
else:
# Accept transaction
balances[account_id] = new_balance
return (balances, rejected)Key Considerations:
-
Deposits (positive amounts) can never cause negative balance
-
Check happens before applying the transaction
-
Rejected transactions don't affect account state
-
Order of rejected transactions matches input order
Part 3: Platform Account Coverage Solution
Strategy:
-
Process transactions with coverage logic
-
When a withdrawal would cause negative balance:
-
Calculate the shortfall (amount needed to bring balance to 0)
-
Deduct from platform account
-
Add coverage to running total
-
Set account balance to 0
-
Return total coverage amount
Time Complexity: O(n)
Space Complexity: O(k)
Example Implementation:
def process_with_coverage(transactions, platform_account_id):
balances = {}
total_coverage = 0
for txn in transactions:
account_id = txn["account_id"]
amount = txn["amount"]
current_balance = balances.get(account_id, 0)
new_balance = current_balance + amount
if new_balance < 0 and account_id != platform_account_id:
# Calculate coverage needed
coverage_needed = -new_balance # Convert negative to positive
# Deduct from platform account
balances[platform_account_id] = balances.get(platform_account_id, 0) - coverage_needed
# Add to total coverage
total_coverage += coverage_needed
# Set account balance to 0
balances[account_id] = 0
else:
balances[account_id] = new_balance
return total_coverageImportant Considerations:
-
Platform account transactions are processed normally
-
Coverage is only for non-platform accounts
-
Shortfall calculation: if balance would be -50, coverage is 50
-
Final balance after coverage is always 0, not the full withdrawal amount
-
Clarify with interviewer: should platform account balance be tracked/validated?