Как уже упоминалось, ожидаемая структура данных не является словарем, поскольку содержит дубликаты ключей.Вместо этого вы можете создать список кортежей.Вы можете использовать объект счетчика и словарь для отслеживания видимых элементов и их соответствующего индекса для создания этого кортежа.
Следующая функция представляет собой понятную реализацию вышеупомянутого метода, который создает ожидаемый результат за одну итерацию.
In [38]: from itertools import count
In [38]: def ranker(lst):
...: for sub in lst:
...: seen = {}
...: c = count()
...: next(c)
...: ss = []
...: for j in sub:
...: try:
...: ind = seen[j]
...: except KeyError:
...: ind = seen[j] = next(c)
...: ss.append((j, ind))
...: yield ss
Демонстрация:
In [39]: lst
Out[39]: [[100, 100, 50, 40, 40, 20, 10, 5], [100, 100, 50, 40, 40, 25, 20, 10]]
In [40]: list(ranker(lst))
Out[40]:
[[(100, 1), (100, 1), (50, 2), (40, 3), (40, 3), (20, 4), (10, 5), (5, 6)],
[(100, 1), (100, 1), (50, 2), (40, 3), (40, 3), (25, 4), (20, 5), (10, 6)]]
Теперь еще один очень питонский, но немного неясный подход состоит в том, чтобы вместо использования блоков counter
и try-except
и даже списка и добавленияиспользуя метод dict.setdefault()
в понимании списка следующим образом:
In [43]: def ranker(lst):
...: for sub in lst:
...: seen = {}
...: yield [(j, seen.setdefault(j, len(seen) + 1)) for j in sub]
Метод dict.setdefault()
возвращает соответствующее значение для первого аргумента, если он присутствует в словаре, в противном случае обновляется со вторым аргументом (len(seen) + 1
) в качестве значения и возвращает его.
Демо:
In [44]: list(ranker(lst))
Out[44]:
[[(100, 1), (100, 1), (50, 2), (40, 3), (40, 3), (20, 4), (10, 5), (5, 6)],
[(100, 1), (100, 1), (50, 2), (40, 3), (40, 3), (25, 4), (20, 5), (10, 6)]]