Частичное решение
names = {age:sorted([name for index,name in enumerate(NAMES) if age == AGES[index]]) for age in sorted(set(AGES))}
print(names)
Возвращает:
{18: ['Cathy', 'Dan'], 19: ['Ed', 'Helen', 'Irene', 'Jack', 'Larry'], 20: ['Alice', 'Frank', 'Gary'], 21: ['Bob'], 22: ['Kelly']}
Оставьте отсортированными (), если хотите. Вы также можете добавить count (), чтобы данные возвращали что-то вроде
{18: [('Cathy',3), ('Dan',7), ...}
, если было 3 Кэти и 7 Данов.
Хотя вышеприведенное выглядит кратко, я подозреваю, что фактический цикл for будет повторять исходные списки меньше.
Или даже лучше:
def names(age):
index = 0
while index < len(AGES):
if AGES[index] == age:
yield NAMES[index]
index += 1
print('19 year olds')
for name in names(19):
print(name)