-1

I've been googling and seaching and I just cant find an answer.

I have a dictionary which holds the people in the group, and the score the group has, groups = {"john,alfred,willis":5, "james,connor":5}, .... People can be in 2 groups at once, however. What I want to do with this dictionary is sort it by the amount of people AND their score. For example:

>>> groups = {"a,b,c":5, "d,e":6, "f,g,h,i":5, "j,k,l":6, "m,n":10, "a,d,f":5}

I need to sort it by the score, then by the amount of people, and then by alphabetical order as a final tie breaker. There are no duplicates of groups, however one group may own "a,b,c,d" and another may own "a,b,c,e". Higher score trumps lower score, more people trumps less people, and alphabetical order is ... alphabetical order.

>>> print(mySort(groups))
"m,n", 10
"j,k,l", 6
"d,e", 6
"f,g,h,i", 5
"a,b,c", 5
"a,d,f", 5

The output format doesn't have to be like that, but it is preferred that it is formatted in the way of a dictionary.

I have attempted a few different ways, including splitting the key by ,'s because the names can be of any length, but because Python isn't my first language, I'm finding it difficult.

How do you sort dictionaries by value and then key size?

EDIT: I have added another part to the question which I thought I could go without. Turns out it's needed though...

TheBrenny
  • 528
  • 3
  • 19

3 Answers3

3

Using sorted: (Return value of the key function is used for comparison)

>>> groups = {"a,b,c":5, "d,e":6, "f,g,h,i":5, "j,k,l":6, "m,n":10}
>>> sorted_keys = sorted(groups, key=lambda k: (groups[k], k), reverse=True)
>>> sorted_keys
['m,n', 'j,k,l', 'd,e', 'f,g,h,i', 'a,b,c']
>>> [(key, groups[key]) for key in sorted_keys]
[('m,n', 10), ('j,k,l', 6), ('d,e', 6), ('f,g,h,i', 5), ('a,b,c', 5)]

UPDATE

key function should be changed as follow to correctly count people.

lambda k: (groups[k], len(k.split(','))), reverse=True)
falsetru
  • 357,413
  • 63
  • 732
  • 636
2
groups = {"a,b,c":5, "d,e":6, "f,g,h,i":5, "j,k,l":6, "m,n":10}
s = sorted(groups.items(),key=lambda x: (x[1],len(x[0])),reverse=True)

for k,v in s:
    print (k,v)
m,n 10
j,k,l 6
d,e 6
f,g,h,i 5
a,b,c 5

use -max(map(ord,x[0]))) to sort by the letter that comes latest in the alphabet, i.e a,b,c,y beats a,b,c,z.

In [37]: groups = {"a,b,c":5, "d,e":6, "f,g,h,i":5, "j,k,l":6, "m,n":10,"a,b,c,d":12,"a,b,c,e":12,"a,b,c,z":13,"a,b,c,y":13}

In [38]: sorted(groups.items(),key=lambda x: (x[1],len(x[0]),-max(map(ord,x[0].split(","))),reverse=True)
Out[38]: 
[('a,b,c,y', 13),
 ('a,b,c,z', 13),
 ('a,b,c,d', 12),
 ('a,b,c,e', 12),
 ('m,n', 10),
 ('j,k,l', 6),
 ('d,e', 6),
 ('f,g,h,i', 5),
 ('a,b,c', 5)]

We use the lambda x: (x[1],len(x[0]),-max(map(ord,x[0].split(",")))) to sort the output above.

In that x[1],len(x[0]) means we sort first on the values x[1] then on the length of each key len(x[0]).

If we are tied on both of those, we go to -max(map(ord,x[0].split(",")), the following is an example of how that works:

If we take "a,b,c,z" and "a,b,c,y" as an example and put them in a list keys =["a,b,c,z","a,b,c,y"]:

First get the ords of each char:

In [54]: ords = [list(map(ord,x.split(","))) for x in keys] # get ord values from each char
In [55]: ords
Out[55]: [[97, 98, 99, 122], [97, 98, 99, 121]]

We are sorting from highest to lowest so we use reverse = True.

In [56]: sorted([max(x) for x in ords], reverse=True) # puts "z" first because of reversing
Out[56]: [122, 121]

So we use -max(x) to reverse that output:

In [57]: sorted([-max(x) for x in ords], reverse=True) # now "y" comes first
Out[57]: [-121, -122]

x in the lambda is each subitem in groups.items() which looks like:

([('a,b,c', 5), ('a,b,c,d', 12), ('j,k,l', 6), ('d,e', 6), ('a,b,c,z', 13), ('m,n', 10), ('a,b,c,y', 13), ('a,b,c,e', 12), ('f,g,h,i', 5)])

So if we take ('a,b,c', 5) x[0] = "a,b,c" and x[1] = 5.

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • Tick: In[38] solved all 3 in one hit. You know your Python very well! Thank you. I'll be renaming things so it doesn't look all too obfuscated. I can learn and understand this. If I have questions, I'll be sure to comment. Thank you. – TheBrenny Aug 12 '14 at 14:58
  • I will add a bit more when I get back on the comp – Padraic Cunningham Aug 12 '14 at 14:59
  • Pretty much everything after lambda. Because I know lambda is an anonymous function, and x is it's parameter. – TheBrenny Aug 12 '14 at 15:00
  • `>>> ords = [list(map(ord,x.split(","))) for x in keys]` doesn't work. Apparently map can't take it's passed arguments. `TypeError: ord() expected a character, but string of length 6 found`. Without this sort of code, though, it takes either the largest letter, so 'xys' would trump 'asdz' (while using min instead of -max) (I was quick on the enter key, as usual) – TheBrenny Aug 14 '14 at 09:01
1

To sort by "amount of people" you rather need

>>> sorted(groups.items(), key=lambda p: (p[1], p[0].count(',')), reverse=True)

[('m,n', 10), ('j,k,l', 6), ('d,e', 6), ('f,g,h,i', 5), ('a,b,c', 5)]

As a side note, comma-separated strings is not the best way to represent groups of things. Consider making your dict tuple-indexed instead:

>>> good_groups = {tuple(k.split(',')):v for k, v in groups.items()}

and then

>>> sorted(good_groups.items(), key=lambda p: (p[1], len(p[0])), reverse=True)

[(('m', 'n'), 10), (('j', 'k', 'l'), 6), (('d', 'e'), 6), (('f', 'g', 'h', 'i'), 5), (('a', 'b', 'c'), 5)]

If your groups are going to be mutated and should be lists, not tuples, you cannot use them as dict keys. Consider a different data structure, for example, a list of dicts:

groups = [
   { 'members': ['foo', 'bar'], 'score': 5 },
   { 'members': ['baz', 'spam'], 'score': 15 },
etc
georg
  • 211,518
  • 52
  • 313
  • 390
  • I dislike tuples because they are immutable - it feels like I have no control over them and I would then have to keep creating new tuples if I want to change the contents... – TheBrenny Aug 12 '14 at 14:28
  • @mastercork889: hmm.... strings are just as immutable - do you hate them because of this? – georg Aug 12 '14 at 14:41
  • I guess I can't 'hate-on' tuples. I just haven't fully learnt how to use them. I have figured that if you turn a string into a list, it can be mutated, and then pushed back into a string. – TheBrenny Aug 12 '14 at 14:43
  • Scary stuff, list/dictionary-inception is. How would I go about getting a value? groups[0][members][1] = bar? – TheBrenny Aug 12 '14 at 14:54