Как достать максимальную и минимальную пару из класса Counter() используя сортировку?

Есть Counter с таким содержимым:

Counter({'Все везде и сразу': 4, 'На Западном фронте без перемен': 2, 'Топ Ган: Мэверик': 2, 'Аватар: Путь воды': 1})

Надо достать это:

Все везде и сразу, 4
Аватар: Путь воды, 1

я сделал по первому и последнему индексу приведя к листу, но интересно как через sorted() это сделать.

Спасибо!


Ответы (3 шт):

Автор решения: Stanislav Volodarskiy

sorted не нужен. c.items() возвращает пары (ключ, значение), key=lambda i: i[1] отбирает значения из пар, min и max по значениям выбирают "минимальную" и "максимальную" пары.

Для максимальных значений есть специальный метод collections.Counter.most_common.

import collections

c = collections.Counter({
    'Все везде и сразу': 4,
    'На Западном фронте без перемен': 2,
    'Топ Ган: Мэверик': 2,
    'Аватар: Путь воды': 1
})

print(min(c.items(), key=lambda i: i[1]))
print(max(c.items(), key=lambda i: i[1]))
print(c.most_common(1)[0])
$ python counter.py
('Аватар: Путь воды', 1)
('Все везде и сразу', 4)
('Все везде и сразу', 4)

P.S. c.most_common()[-1] вернёт самый непопулярный фильм. Но вызов most_common без параметра приводит к сортировке, чего лучше избегать (время и память не бесплатны).

P.P.S. Что будет если самых популярных фильмов больше одного? Текущее решение вернёт какой-то один. А надо бы возвращать все самые пулярные:

import collections

c = collections.Counter({
    'Все везде и сразу': 4,
    'Ещё один лидер проката': 4,
    'На Западном фронте без перемен': 2,
    'Топ Ган: Мэверик': 2,
    'Аватар: Путь воды': 1,
    'Ещё один неудачник проката': 1
})

min_value = min(c.values())
print(*filter(lambda i: i[1] == min_value, c.items()))

max_value = max(c.values())
print(*filter(lambda i: i[1] == max_value, c.items()))
$ python counter.py
('Аватар: Путь воды', 1) ('Ещё один неудачник проката', 1)
('Все везде и сразу', 4) ('Ещё один лидер проката', 4)
→ Ссылка
Автор решения: Ben Puls
from collections import Counter

counter = Counter({
    'Все везде и сразу': 4,
    'На Западном фронте без перемен': 2,
    'Топ Ган: Мэверик': 2,
    'Аватар: Путь воды': 1
})

Для этого нужно просто сортировать не по ключу в словаре, а по значению. Для этого в функции sorted() существует аргумент key.

Сначала превращаем словарь в список, состоящий из кортежей ("key", "value"). А затем, через лямбда-функцию выбираем то, по чему мы будем сортировать, то есть последний элемент кортежа:

sorted_pairs = sorted(counter.items(), key=lambda x: x[1])

Соотвественно, пара с наибольшим значением будет первой в списке, с наименьшим - в конце

И тогда получаем:

min_pair, max_pair = sorted_pairs[0], sorted_pairs[-1]

print(*min_pair) 
print(*max_pair)

Все везде и сразу 4

Аватар: Путь воды 1

→ Ссылка
Автор решения: Vladimir Bogdanov

Пара тонких моментов:

  1. lambda - это очень медленно.
  2. Метод items() создает в памяти список кортежей с парами ключ-значение для всего содержимого словаря, т.е. фактически дублируем словарь в памяти.

Более эффективный вариант, хотя и не идеальный. Существует вариант еще быстрее, но несколько громоздкий и требует написание функции.

from operator import itemgetter

print(min(iter(c.items()), key=itemgetter(1)))
print(max(iter(c.items()), key=itemgetter(1)))
→ Ссылка