This post shows an interview-ready way to solve a common “aggregate + filter + rank” problem in Java. We’ll go from the problem statement to a clean final solution, and then cover tips, pitfalls, and improvements that interviewers typically look for.
✅ Question
A company wants to reward the Top 5 sales executives based on performance over the last 3 quarters.
Data Files
q1_sales.csvq2_sales.csvq3_sales.csv
Each Record
(employeeId, dealsClosed, revenue)
Selection Criteria
An employee is eligible only if all conditions are met:
Total deals closed ≥ 30 across all quarters
Average revenue per deal > $5,000
From eligible employees, select Top 5 by total revenue (descending)
Output
Return a list of employeeIds sorted by total revenue DESC.
๐ง Approach
This is an “aggregation + eligibility + ranking” problem. The clean approach is:
Aggregate quarter data into one summary per employee.
Track: deals and revenue per quarter, and totals.
Apply filters:
totalDeals ≥ 30
dealsQ1 ≥ 5, dealsQ2 ≥ 5, dealsQ3 ≥ 5
averageRevenuePerDeal > 5000
Sort by total revenue descending
Take top 5
Return list of employeeIds
Why Map?
A HashMap<String, EmployeeSalesSummary> gives you O(1) updates per record and keeps the code straightforward.
✅ Final Answer (Clean Java Solution)
This is a polished version of the code you started, with small improvements:
Uses constants for rule thresholds
Fixes the placeholder return (now returns top list)
Ensures filters match the problem statement
Adds a tie-breaker for consistent sorting (optional but good practice)
Removes unused imports
import java.util.*;
import java.util.stream.Collectors;
public class TopSalesExecutivesFinder {
// ---------------- SAMPLE INPUT (Hardcoded) ----------------
static List<SalesRecord> q1Sales = Arrays.asList(
new SalesRecord("E1", 10, 60000),
new SalesRecord("E2", 8, 30000),
new SalesRecord("E3", 12, 80000)
);
static List<SalesRecord> q2Sales = Arrays.asList(
new SalesRecord("E1", 9, 55000),
new SalesRecord("E2", 12, 70000),
new SalesRecord("E3", 10, 60000)
);
static List<SalesRecord> q3Sales = Arrays.asList(
new SalesRecord("E1", 11, 65000),
new SalesRecord("E2", 10, 50000),
new SalesRecord("E3", 9, 55000)
);
public static List<String> findTopSalesExecutives() {
// ---------------- RULE THRESHOLDS ----------------
final int MIN_TOTAL_DEALS = 30;
final int MIN_DEALS_EACH_QUARTER = 5;
final double MIN_AVG_REV_PER_DEAL = 5000.0; // must be strictly greater
final int TOP_K = 5;
// ---------------- AGGREGATION ----------------
Map<String, EmployeeSalesSummary> summaryMap = new HashMap<>();
q1Sales.forEach(r ->
summaryMap.computeIfAbsent(r.employeeId, EmployeeSalesSummary::new)
.addQ1(r.dealsClosed, r.revenue)
);
q2Sales.forEach(r ->
summaryMap.computeIfAbsent(r.employeeId, EmployeeSalesSummary::new)
.addQ2(r.dealsClosed, r.revenue)
);
q3Sales.forEach(r ->
summaryMap.computeIfAbsent(r.employeeId, EmployeeSalesSummary::new)
.addQ3(r.dealsClosed, r.revenue)
);
// ---------------- FILTER + SORT + PICK ----------------
return summaryMap.values().stream()
// 1) total deals >= 30
.filter(s -> s.totalDeals() >= MIN_TOTAL_DEALS)
// 2) deals >= 5 in each quarter
.filter(s -> s.dealsEachQuarterAtLeast(MIN_DEALS_EACH_QUARTER))
// 3) avg revenue per deal > 5000
.filter(s -> s.averageRevenuePerDeal() > MIN_AVG_REV_PER_DEAL)
// 4) sort by total revenue desc (tie-breaker optional)
.sorted(Comparator.comparingDouble(EmployeeSalesSummary::totalRevenue).reversed()
.thenComparing(EmployeeSalesSummary::totalDeals, Comparator.reverseOrder())
.thenComparing(s -> s.employeeId))
// 5) top 5
.limit(TOP_K)
// return employeeIds
.map(s -> s.employeeId)
.collect(Collectors.toList());
}
public static void main(String[] args) {
List<String> result = findTopSalesExecutives();
System.out.println("Top Sales Executives: " + result);
}
}
// ---------------- SUPPORT CLASSES ----------------
class SalesRecord {
String employeeId;
int dealsClosed;
double revenue;
SalesRecord(String employeeId, int dealsClosed, double revenue) {
this.employeeId = employeeId;
this.dealsClosed = dealsClosed;
this.revenue = revenue;
}
}
class EmployeeSalesSummary {
String employeeId;
int dealsQ1, dealsQ2, dealsQ3;
double revenueQ1, revenueQ2, revenueQ3;
EmployeeSalesSummary(String employeeId) {
this.employeeId = employeeId;
}
void addQ1(int deals, double revenue) {
dealsQ1 += deals;
revenueQ1 += revenue;
}
void addQ2(int deals, double revenue) {
dealsQ2 += deals;
revenueQ2 += revenue;
}
void addQ3(int deals, double revenue) {
dealsQ3 += deals;
revenueQ3 += revenue;
}
int totalDeals() {
return dealsQ1 + dealsQ2 + dealsQ3;
}
double totalRevenue() {
return revenueQ1 + revenueQ2 + revenueQ3;
}
double averageRevenuePerDeal() {
int totalDeals = totalDeals();
if (totalDeals == 0) return 0.0;
return totalRevenue() / totalDeals; // safe double division
}
boolean dealsEachQuarterAtLeast(int minDeals) {
return dealsQ1 >= minDeals && dealsQ2 >= minDeals && dealsQ3 >= minDeals;
}
@Override
public String toString() {
return "EmployeeSalesSummary{" +
"employeeId='" + employeeId + '\'' +
", dealsQ1=" + dealsQ1 +
", dealsQ2=" + dealsQ2 +
", dealsQ3=" + dealsQ3 +
", totalDeals=" + totalDeals() +
", totalRevenue=" + totalRevenue() +
", avgRevPerDeal=" + averageRevenuePerDeal() +
'}';
}
}
๐งพ What’s the Output for Your Sample Data?
With your sample input:
E1 total deals = 10 + 9 + 11 = 30 ✅
avg revenue per deal = (60000+55000+65000)/30 = 180000/30 = 6000 ✅
each quarter deals ≥ 5 ✅
total revenue = 180000E2 total deals = 8 + 12 + 10 = 30 ✅
avg revenue per deal = (30000+70000+50000)/30 = 150000/30 = 5000 ❌ (must be > 5000)E3 total deals = 12 + 10 + 9 = 31 ✅
avg revenue per deal = (80000+60000+55000)/31 ≈ 6290 ✅
each quarter deals ≥ 5 ✅
total revenue = 195000
✅ Result:
Top Sales Executives: [E3, E1]
✅ Tips & Interview Notes
1) Don’t mix up “average revenue per deal”
The rule is:
average revenue per deal across ALL quarters
So compute:
totalRevenue / totalDeals
Not quarter averages.
2) Beware >= vs >
The problem says average revenue per deal > 5000
That means 5000 exactly should FAIL (like E2 above)
3) Avoid integer division
Here we’re safe because totalRevenue is double.
If revenue was int, you’d need:
(double) totalRevenue / totalDeals
4) Sorting tie-breakers (bonus)
If total revenue ties, you can stabilize output:
higher total deals wins
then alphabetical employeeId
Interviewers like deterministic sorting.
5) Make rules configurable
Putting thresholds in constants makes the function reusable:
change to Top 10
change min deals from 30 to 20
etc.
⏱ Complexity
Let N be total number of sales records across q1, q2, q3, and P employees.
Aggregation: O(N)
Sorting: O(P log P)
Memory: O(P)
This is optimal for the problem.
Optional Enhancements (If Asked)
Read from actual CSVs using
BufferedReaderSupport any number of quarters (use arrays instead of
dealsQ1/Q2/Q3)Add unit tests:
avg exactly 5000
missing quarter data
fewer than 5 eligible employees