OOP Challenge
Learning intentions
In this lesson, you will build a small sales workflow for a video rental store using objects and collection functions.
- Use
map to transform rental data into receipt data.
- Use
filter to select only overdue cases for billing.
- Use
reduce to total overdue fee income.
- Build your own object that conforms to
CustomStringConvertible, Equatable, and Comparable.
- Prepare your objects for storage by conforming to
Hashable and Codable.
You will receive one point in 6-7-Eleven for each task you complete.
Scenario
You are helping a local video rental store prepare end-of-week records.
- Each rental has a customer, a video, and return status.
- The store charges a flat
$2.00 overdue fee when a rental is not returned on time.
- Your goal is to generate receipts, find overdue customers, total overdue income, and save billing records.
Use this code as your starting point for all five tasks.
import Foundation
struct Video: Identifiable {
let id: UUID
let title: String
let dailyRate: Double
}
struct Customer: Identifiable {
let id: UUID
let name: String
let address: String
}
struct VideoRental {
let videoID: Video.ID
let customerID: Customer.ID
let dayIssued: Int
let dayToReturn: Int
let wasReturned: Bool
}
struct Receipt {
let videoID: Video.ID
let customerID: Customer.ID
let pricePaid: Double
let overdueFeeCharged: Bool
}
let videos: [Video] = [
Video(id: UUID(), title: "The Matrix", dailyRate: 4.50),
Video(id: UUID(), title: "Toy Story", dailyRate: 3.00),
Video(id: UUID(), title: "Spirited Away", dailyRate: 4.00),
Video(id: UUID(), title: "Interstellar", dailyRate: 5.00),
Video(id: UUID(), title: "Moana", dailyRate: 3.50)
]
let customers: [Customer] = [
Customer(id: UUID(), name: "Aroha Ngata", address: "14 Kowhai Street"),
Customer(id: UUID(), name: "Liam Patel", address: "8 Tui Avenue"),
Customer(id: UUID(), name: "Mia Thompson", address: "22 Rimu Road"),
Customer(id: UUID(), name: "Noah Wiremu", address: "3 Pukeko Lane"),
Customer(id: UUID(), name: "Eva Chen", address: "11 Nikau Place")
]
let rentals: [VideoRental] = [
VideoRental(videoID: videos[0].id,
customerID: customers[0].id,
dayIssued: 1, dayToReturn: 3,
wasReturned: true),
VideoRental(videoID: videos[1].id,
customerID: customers[1].id,
dayIssued: 2, dayToReturn: 4,
wasReturned: false),
VideoRental(videoID: videos[2].id,
customerID: customers[2].id,
dayIssued: 2, dayToReturn: 5,
wasReturned: true),
VideoRental(videoID: videos[3].id,
customerID: customers[3].id,
dayIssued: 3, dayToReturn: 6,
wasReturned: false),
VideoRental(videoID: videos[4].id,
customerID: customers[4].id,
dayIssued: 4, dayToReturn: 6,
wasReturned: true)
]
Task A
Map rentals to receipts, then print receipts.
Complete the following:
Use map to build a [Receipt] from rentals.
Use a for loop to print each receipt in this format: Receipt | Customer: <name> | Video: <title> | Base: $<price> | Overdue: <Yes/No>
Format money to 2 decimal places.
Task B
Filter overdue receipts, then print billing addresses.
Complete the following:
Use filter to keep only receipts where overdueFeeCharged == true.
Use a loop to print a mailing list in this format: Send overdue notice to: <customer name>, <address>
Use the customers data (via customerID) to find addresses.
Task C
The store owner wants one number: total dollars earned from overdue fees for this data set.
Complete the following:
- Use
reduce on the overdueReceipts array.
- Add
$2.00 for each overdue receipt.
- Print the final total in this format:
Total overdue fees collected: $<amount>
Task D
Create your own object to handle customer billing.
Complete the following:
- Create
CustomerBill with the required fields and protocol conformances.
- Properties:
customer: Customer, receipt: Receipt — it accepts the actual objects as arguments, not just the ID
- Protocol conformance:
CustomStringConvertible, Equatable, Sortable
- Use
map to convert overdue receipts into [CustomerBill].
- Sort the bills array from highest fee to lowest using
.sorted().reversed().
- Print only the first bill to verify your
description output.
Use this letter format for description:
Kia ora <customer name>,
Our records show that "<video title>" was overdue.
Base rental paid: $<base price>
Overdue fee now due: $<fee amount>
Please pay this amount at your earliest convenience.
Store Billing Team
Task E
Make all models Hashable and Codable in order to store bills on disk.
Complete the following:
- Add
Hashable, Codable conformance to all five model types.
- Encode
[CustomerBill] using JSONEncoder.
- Save to
customer_bills.json.
- Read the file back and print it to confirm the JSON contains all bill data.
Use this code to store your data to a JSON file.
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
do {
let data = try encoder.encode(bills)
let url = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
.appendingPathComponent("customer_bills.json")
try data.write(to: url)
print("Saved JSON to: \(url.path)")
let readBack = try String(contentsOf: url, encoding: .utf8)
print(readBack)
} catch {
print("JSON save/load failed: \(error)")
}
Extension challenge for Super Players!
Those who complete this section before Friday will receive DOUBLE points.
- Add one more field to
Receipt called daysOverdue and change the fee rule from flat $2.00 to $2.00 per day overdue.
- Update your
map, filter, reduce, and CustomerBill logic to use the new fee rule.