Маючи так багато запропонованих рішень, я вражений, що ніхто не запропонував те, що я вважав би очевидним (для непридатних, але порівняних елементів) - [ itertools.groupby] [1]. itertoolsпропонує швидку функціональність для багаторазового використання та дозволяє делегувати деяку складну логіку добре перевіреним стандартним компонентам бібліотеки. Розглянемо для прикладу:
import itertools
import operator
def most_common(L):
# get an iterable of (item, iterable) pairs
SL = sorted((x, i) for i, x in enumerate(L))
# print 'SL:', SL
groups = itertools.groupby(SL, key=operator.itemgetter(0))
# auxiliary function to get "quality" for an item
def _auxfun(g):
item, iterable = g
count = 0
min_index = len(L)
for _, where in iterable:
count += 1
min_index = min(min_index, where)
# print 'item %r, count %r, minind %r' % (item, count, min_index)
return count, -min_index
# pick the highest-count/earliest item
return max(groups, key=_auxfun)[0]
Це, звичайно, можна написати більш стисло, але я прагну до максимальної ясності. Два printтвердження можна коментувати, щоб краще бачити механізм дії; наприклад, з відбитками без коментарів:
print most_common(['goose', 'duck', 'duck', 'goose'])
випускає:
SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
Як бачите, SLце список пар, кожна пара - предмет, за яким іде індекс елемента в початковому списку (щоб реалізувати ключову умову, що, якщо "найпоширеніші" елементи з однаковим найбільшим числом> 1, результат повинен бути бути найбільш ранньою).
groupbyгрупи лише за пунктом (через operator.itemgetter). Допоміжна функція, що викликається один раз на групування під час maxобчислення, отримує та внутрішньо розпаковує групу - кортеж з двома елементами, (item, iterable)де елементи ітерабеля також є двоярусними кортежами, (item, original index)[[пункти SL]].
Тоді допоміжна функція використовує цикл, щоб визначити як кількість записів у групі ітерабелів, так і мінімальний вихідний індекс; він повертає ті, що є комбінованим "ключем якості", зі знаком мінімального індексу, зміненим, тому maxоперація вважатиме "кращими" ті елементи, які відбулися раніше в первинному списку.
Цей код може бути набагато простішим, якби він трохи менше турбував проблеми з великим O у часі та просторі, наприклад ...:
def most_common(L):
groups = itertools.groupby(sorted(L))
def _auxfun((item, iterable)):
return len(list(iterable)), -L.index(item)
return max(groups, key=_auxfun)[0]
така ж основна ідея, що висловлюється просто і компактно ... але, на жаль, додатковий допоміжний простір O (N) (для втілення ітерабелів груп у списки) та час (N у квадраті) для отримання L.indexкожного елемента) . Хоча передчасна оптимізація є коренем усього зла в програмуванні, свідомо вибирати підхід O (N у квадраті), коли доступний O (N log N) просто надто сильно проти зерна масштабованості! -)
Нарешті, для тих, хто віддає перевагу чіткості та продуктивності "oneliners", бонусна версія 1-лайнера з відповідними накрученими назвами :-).
from itertools import groupby as g
def most_common_oneliner(L):
return max(g(sorted(L)), key=lambda(x, v):(len(list(v)),-L.index(x)))[0]