Build Ranked Feed With Photo Batching
Company: Nextdoor
Role: Machine Learning Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates algorithmic reasoning and data manipulation skills, including stable sorting, grouping/batching, and merging of ranked items while preserving ordering invariants.
Constraints
- 0 <= len(feed_objects) <= 100000
- Each object has unique `id`
- Each `type` is one of `Normal`, `Video`, or `Photo`
- Scores are numeric values and may be negative or duplicated
Examples
Input: ([{'id': 'N1', 'type': 'Normal', 'score': 100}, {'id': 'N2', 'type': 'Normal', 'score': 95}, {'id': 'P1', 'type': 'Photo', 'score': 90}, {'id': 'P2', 'type': 'Photo', 'score': 89}, {'id': 'V', 'type': 'Video', 'score': 88}, {'id': 'P3', 'type': 'Photo', 'score': 87}, {'id': 'P4', 'type': 'Photo', 'score': 80}, {'id': 'N3', 'type': 'Normal', 'score': 70}],)
Expected Output: ['N1', 'N2', ['P1', 'P2', 'P3'], 'V', ['P4'], 'N3']
Explanation: After ranking, photos are P1, P2, P3, P4. They become batches [P1, P2, P3] with score 90 and [P4] with score 80, then those batches are merged with non-photo items by score.
Input: ([{'id': 'P1', 'type': 'Photo', 'score': 50}, {'id': 'N1', 'type': 'Normal', 'score': 50}, {'id': 'P2', 'type': 'Photo', 'score': 50}, {'id': 'V1', 'type': 'Video', 'score': 40}, {'id': 'P3', 'type': 'Photo', 'score': 30}],)
Expected Output: [['P1', 'P2', 'P3'], 'N1', 'V1']
Explanation: The stable ranked order keeps P1 before N1 before P2 among score-50 items. The batch [P1, P2, P3] gets score 50 and inherits P1's earlier ranked position, so it appears before N1.
Input: ([{'id': 'P1', 'type': 'Photo', 'score': 10}, {'id': 'P2', 'type': 'Photo', 'score': 30}, {'id': 'P3', 'type': 'Photo', 'score': 20}, {'id': 'P4', 'type': 'Photo', 'score': 30}],)
Expected Output: [['P2', 'P4', 'P3'], ['P1']]
Explanation: After stable sorting by score descending, the photo order is P2, P4, P3, P1. Grouping by 3 gives [P2, P4, P3] and [P1].
Input: ([],)
Expected Output: []
Explanation: An empty input list produces an empty final feed.
Input: ([{'id': 'V1', 'type': 'Video', 'score': 90}, {'id': 'P1', 'type': 'Photo', 'score': 90}, {'id': 'N1', 'type': 'Normal', 'score': 85}, {'id': 'P2', 'type': 'Photo', 'score': 80}, {'id': 'P3', 'type': 'Photo', 'score': 70}, {'id': 'P4', 'type': 'Photo', 'score': 60}],)
Expected Output: ['V1', ['P1', 'P2', 'P3'], 'N1', ['P4']]
Explanation: V1 and the first photo batch both have score 90, but V1 appears earlier in the ranked list, so it stays before the batch.
Input: ([{'id': 'N1', 'type': 'Normal', 'score': -1}, {'id': 'V1', 'type': 'Video', 'score': 5}, {'id': 'N2', 'type': 'Normal', 'score': 5}],)
Expected Output: ['V1', 'N2', 'N1']
Explanation: There are no photos, so the result is just the stable score-descending order of the non-photo items.
Hints
- First create one stable ranked list by sorting on score descending. Python's sort is stable, which helps with tie-breaking.
- Treat each photo batch as a synthetic item whose score and position come from its first photo, then merge those synthetic items with non-photo items.