A high-performance C++ tool that parses CSV files of millions of ride-share trip records and identifies the busiest pickup zones and busiest operating hours — built to stay correct on malformed data and fast enough to process 5+ million rows within strict time limits.
Given a CSV of trip records, the analyzer answers two questions:
- Which pickup zones have the most trips?
- Which (zone, hour) combinations are the busiest?
The core constraint driving the design wasn't just correctness — it was correctness at scale under adversarial, dirty input. The system was built and tested against inputs specifically designed to break naive implementations: malformed rows, boundary hour values, and millions of rows engineered to defeat any O(n²) approach.
std::unordered_map<std::string, long long> m_zoneCounts;
std::unordered_map<std::string, std::unordered_map<int, long long>> m_hourlyCounts;Why unordered_map over map or vector:
| Approach | Per-row cost | For millions of rows |
|---|---|---|
vector + linear search |
O(n) per lookup | O(n²) total — times out |
map (balanced tree) |
O(log n) per lookup | Correct, but constant-factor overhead adds up |
unordered_map (hash table) |
O(1) average | O(n) total — the only option that scales |
Only aggregated counts are stored — never individual trip rows — so memory stays bounded even on datasets with 5 million+ rows.
Complexity:
- Ingestion: O(n) — n = number of CSV rows, O(1) average per hash map insert
- Ranking: O(m log m) — m = number of unique zones, always ≤ n, so sorting is cheaper than ingestion
- Lookup: O(1) average
ingestFile never crashes on bad input, by design:
- Rows with fewer than 6 columns are skipped
- Empty zone IDs are skipped
- Date/time parsing is wrapped in a
try/catch— a malformed timestamp (e.g. non-numeric hour) causes that single row to be skipped, not the whole program to crash - A missing input file is handled gracefully — returns empty results rather than throwing
try {
int hour = std::stoi(timeStr.substr(spacePos + 1, 2));
if (hour < 0 || hour > 23) return;
zoneCounts[pickupZone]++;
hourlyCounts[pickupZone][hour]++;
} catch (...) {
return; // skip this row, keep processing the rest of the file
}Results are always sorted with an explicit tie-breaking rule, so output is reproducible regardless of hash map iteration order:
- Top zones: count descending, then zone ID ascending
- Top busy slots: count descending, then zone ID ascending, then hour ascending
std::sort(result.begin(), result.end(), [](const ZoneCount& a, const ZoneCount& b) {
if (a.count != b.count) return a.count > b.count;
return a.zone < b.zone;
});Requires a C++17 compiler.
make
./mainThis runs the reference driver (main.cpp), which loads SmallTrips.csv,
prints the top 10 zones and top 10 busy slots, and reports execution time.
TripID,PickupZoneID,DropoffZoneID,PickupDateTime,DistanceKm,FareAmount
1000001,ZONE254,ZONE819,2024-01-01 00:00,16.0,74.9
class TripAnalyzer {
public:
void ingestFile(const std::string& csvPath);
void ingestStdin(); // reads from stdin, same parsing logic
std::vector<ZoneCount> topZones(int k = 10) const;
std::vector<SlotCount> topBusySlots(int k = 10) const;
};- No automated test suite is included in this repository (see Notes below).
- Hour is parsed from a fixed
YYYY-MM-DD HH:MMposition in the string rather than a general-purpose date parser — sufficient for this dataset's format, but not robust to other timestamp formats. - Zone IDs are treated as case-sensitive by design (
ZONE01andzone01are counted separately) — this matches the project's stated requirements, but would need revisiting for a real-world deployment where that distinction might be unintended.
- Add a self-written test suite covering malformed input, tie-breaking, and larger-scale aggregation correctness.
- Support configurable CSV schemas rather than a fixed 6-column format.
- Add multithreaded ingestion for very large files.
This was a team project for CMP2003 (Data Structures and Algorithms) at Bahçeşehir University. Contributions, as documented in the source and the report:
- Walid Halabi — CSV ingestion and parsing (
ingestFile,processLine), robustness against malformed data - Tareq Iyad Mohammed Akram Shwaika —
topZonesranking and sorting - Yousef Salama —
topBusySlotsranking and sorting
Published with the agreement of all team members and the course instructor. The original assignment's grading test suite and CI configuration are not included, since they are the instructor's material and are reused across course terms.
Full methodology and complexity analysis: report.pdf.
MIT — see LICENSE.
This repository contains coursework completed for CMP2003 at Bahçeşehir University. If you are taking this or a similar course, do not submit any part of this work as your own.