Problem overview
OldestPet gets a List<(string Name, int Age)> named pets: a list whose items are name-and-age pairs like ("Rex", 4). It returns the name, as a string, of the pet with the highest age. If several pets share the highest age, the first of them in the list wins. The list always has at least one pet.
For [("Rex", 4), ("Bella", 7), ("Max", 2)] the answer is "Bella". For [("Luna", 3), ("Coco", 9), ("Milo", 9)] it is "Coco": Coco and Milo are both 9, and Coco comes first.
Approach
This is the "largest so far" pattern from a plain list of numbers, with one change: instead of keeping the biggest age, keep the whole pair. Then the name is still there when the loop ends, and no second search is needed.
Start with the first pet as the champion. Then compare every pet with the current champion and replace it only when the new pet is strictly older. A pet of the same age never replaces the champion, so on a tie the earlier pet stays. That one choice, > and not >=, satisfies the tie rule.
Step by step
- Declare
oldestwithvarand set it topets[0]. The type becomes(string Name, int Age)automatically, names included. - Loop with
foreach (var pet in pets). The loop variable is a tuple too. - Compare ages:
pet.Age > oldest.Age. - Replace the champion:
oldest = pet;. This copies the whole pair, name and age together. - Return
oldest.Name. The method returns the name only, not the pair.
The solution in C#
class Solution {
public string OldestPet(List<(string Name, int Age)> pets) {
var oldest = pets[0];
foreach (var pet in pets) {
if (pet.Age > oldest.Age) {
oldest = pet;
}
}
return oldest.Name;
}
}
The loop starts with oldest and the first pet being the same pair, so the first comparison is a pet against itself and nothing changes. That is harmless. Starting the loop at index 1 would skip it, but then you would need a for loop instead of a foreach.
Another way
LINQ has a ready-made version of this loop. pets.MaxBy(p => p.Age).Name returns the first pet with the highest age, so on [("Luna", 3), ("Coco", 9), ("Milo", 9)] it gives Coco, like the loop. So does pets.OrderByDescending(p => p.Age).First().Name, because the sort is stable and keeps tied pets in their original order. The loop in the lesson is worth writing by hand first: it shows exactly what MaxBy does, and it works the same in any language.
Complexity
One pass over n pets with a constant amount of work each: O(n) time and O(1) extra space.
Testing
Results from running the solution:
| pets | Result |
|---|---|
[("Rex", 4), ("Bella", 7), ("Max", 2)] | Bella |
[("Luna", 3), ("Coco", 9), ("Milo", 9)] | Coco |
[("Kiki", 0)] | Kiki |
Common mistakes
- Using
>=. It makes the last of the tied pets win, so the second example would give"Milo". - Keeping only the age. If
oldestis just anint, you know how old the oldest pet is but not who it is. - Starting the champion at age 0. A pet can be 0 years old, as in the third example, and a made-up starting pair would need a name. Starting from
pets[0]avoids both problems.
