Печатать верхние "n" записи в словаре

Здесь мой текущий код

pN ={'dave': 10, 'jacinta': 10, 'james': 8, 'john': 6, 'jack': 3, 'sam': 2}
highestCount = max(pN.values())

for k, v in pN.items():
  if v == highestCount:
    print(v,k)

Однако это только печатает верхнего пользователя и, если эта позиция является общей, печатает ее снова как таковой

10 dave
10 jacinta

Мне нужно иметь возможность печатать любое количество лучших пользователей (n) и форматировать их как таковые, например, для n = 5:

10 john, jacinta, 
8 james
6 john
3 jack
2 sam

Ответ 1

Используйте команду collections.defaultdict, поменяйте свои keys и values

from collections import defaultdict
dct = defaultdict(list)

for k, v in pN.items():
  dct[v].append(k)

# defaultdict(<class 'list'>, {10: ['dave', 'jacinta'], 8: ['james'], 6: ['john'], 3: ['jack'], 2: ['sam']})

Используйте sorted для вывода:

for k, v in sorted(dct.items(), reverse=True):
  print(k, ', '.join(v))

# Result

10 dave, jacinta
8 james
6 john
3 jack
2 sam

function для возврата top n пользователей (рассматривает связь как одну запись):

def top_n(d, n):
  dct = defaultdict(list) 
  for k, v in d.items():
    dct[v].append(k)      
  return sorted(dct.items())[-n:][::-1]

top_n(pN, 3)

# [(10, ['dave', 'jacinta']), (8, ['james']), (6, ['john'])]

Использование defaultdict прост и быстр, и вот некоторые моменты, чтобы доказать это:

Функции, которые будут синхронизированы

def chris_z(d, n):
  dct = defaultdict(list) 
  for k, v in d.items():
    dct[v].append(k)      
  return sorted(dct.items())[-n:][::-1]

def tim_lombard(score_dict, n):
  lot = [(k,v) for k, v in score_dict.items()] #make list of tuple from scores dict
  nl = []
  while len(lot)> 0:
      nl.append(max(lot, key=lambda x: x[1]))
      lot.remove(nl[-1])

def ajax(d, n:'n_users', top = True):
  _ranks = sorted(d.values())
  _ranks = _ranks[-n:] if top else _ranks[:n]
  return {i:[a for a, b in d.items() if b == i] for i in _ranks}

Результаты

x = [''.join(i) for i in itertools.permutations('chrisz', 6)]    
y = [random.randint(0, 100) for _ in range(720)]  
z = dict(zip(x, y))

In [40]: %timeit chris_z(z, 500)
110 µs ± 259 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [42]: %timeit tim_lombard(z, 500)
26.2 ms ± 60 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [43]: %timeit ajax(z, 500)
15.3 ms ± 227 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Ответ 2

Вы можете использовать sorted и понимание словаря:

from typing import Dict, List
def ranking(d, n:'n_users', top = True) -> Dict[int, List[str]]:
  _ranks = sorted(d.values())
  _ranks = _ranks[-n:] if top else _ranks[:n]
  return {i:[a for a, b in d.items() if b == i] for i in _ranks}

pN ={'dave': 10, 'jacinta': 10, 'james': 8, 'john': 6, 'jack': 3, 'sam': 2}
for a, b in sorted(ranking(pN, 10).items(), key=lambda x:x[0], reverse=True):
  print('{} {}'.format(a, ', '.join(b)))

Выход:

10 dave, jacinta
8 james
6 john
3 jack
2 sam

Изменение: для любого числа самых популярных пользователей передайте значение функции:

_r = ranking(pN, 5) #for the top 5 users

Ответ 3

Будет ли это работать для вас?

pN ={'dave': 10, 'jacinta': 10, 'james': 8, 'john': 6, 'jack': 3, 'sam': 2}

def top_n_scores(n, score_dict):
  ''' returns the n scores from a name:score dict'''
  lot = [(k,v) for k, v in pN.items()] #make list of tuple from scores dict
  nl = []
  while len(lot)> 0:
      nl.append(max(lot, key=lambda x: x[1]))
      lot.remove(nl[-1])

  return nl[0:n]   

Чтобы получить лучшие 4 балла:

top_n_scores(4, pN) 

[('dave', 10), ('jacinta', 10), ('james', 8), ('john', 6)]

Ответ 4

Вы должны попробовать его с помощью collections.defaultdict с встроенной функцией sorted sorted().

from collections import defaultdict

def sort_it(_dict, n):
    result = defaultdict(list)
    for name, num in _dict.items():
        result[num].append(name)
    return sorted(result.items(), reverse=True)[:n]

>>> pN = {'dave': 10, 'jacinta': 10, 'james': 8, 'john': 6, 'jack': 3, 'sam': 2}
>>> top3 = sort_it(pN, 3)
[(10, ['jacinta', 'dave']), (8, ['james']), (6, ['john'])] # Output

Ответ 5

Поскольку вы хотите группировать по баллам, было бы целесообразно использовать itertools.groupby:

scores ={'dave': 10, 'jacinta': 10, 'james': 8, 'john': 6, 'jack': 3, 'sam': 2}

from itertools import groupby
from operator import itemgetter

get_score = itemgetter(1)

def group_users_by_score(scores, n=-1):
    sorted_users = sorted(scores.items(), key=get_score, reverse=True)
    top_n = sorted_users[:n]
    return groupby(top_n, key=get_score)

def display_top_users(scores, n=-1):
    for score, users in group_users_by_score(scores, n):
        print("%3d %s" % (score, ', '.join(u for (u,s) in users)))

В качестве примера:

>>> display_top_users(scores, 3)
 10 dave, jacinta
  8 james
>>> display_top_users(scores)
 10 dave, jacinta
  8 james
  6 john
  3 jack