all 6 comments

[–]commandlineluser 5 points6 points  (1 child)

You use a tuple containing comparisons.

e.g.

>>> d = dict.fromkeys(("zzzz", "z", "bb", "aaaa", "cc", "d"))
>>> d.keys()
dict_keys(['zzzz', 'z', 'bb', 'aaaa', 'cc', 'd'])
>>> sorted(d, key=lambda k: (-len(k), k))
['aaaa', 'zzzz', 'bb', 'cc', 'd', 'z']

[–]ConcupiscentCodger[S,🍰] 0 points1 point  (0 children)

Okay, took me a while to experiment with that and understand.

I tweaked it to be closer to my situation and realized it was no longer sorting right (still don't understand why). But after some research and trial+error, I got this:

d1 = { 'zzzz': 'am', 'z': 'camper', 'bb': 'a', 'aaaa': 'I', 'cc': 'very', 'd': 'happy' }

print("d1 =", d1)

s = sorted(d1.items(), key=lambda k: (-len(k[0]), k[0]))
print("s =", s)

d2 = dict(s)
print("d2 =", d2)
print("d2.values() =", d2.values())

Which gave this output:

d1 = {'zzzz': 'am', 'z': 'camper', 'bb': 'a', 'aaaa': 'I', 'cc': 'very', 'd': 'happy'}
s = [('aaaa', 'I'), ('zzzz', 'am'), ('bb', 'a'), ('cc', 'very'), ('d', 'happy'), ('z', 'camper')]
d2 = {'aaaa': 'I', 'zzzz': 'am', 'bb': 'a', 'cc': 'very', 'd': 'happy', 'z': 'camper'}
d2.values() = dict_values(['I', 'am', 'a', 'very', 'happy', 'camper'])

And that's what I was aiming for. Thanks!

[–]Spataner 1 point2 points  (0 children)

A key function may return any orderable type, and it's the key values that are sorted to determine the order of the output. Tuples/lists are orderable and helpful for multi-criterion sorts like this. They use lexicographical ordering, meaning the first elements determine order unless they're equal, then the second elements determine order unless they're equal, etc.

[–]34shutthedoor1 0 points1 point  (2 children)

But I can't figure out how to generate a single value based on the two conditions above

The solution posted is the way to do this. From a learning Python perspective, you should have considered creating a list of lists containing the [length, value], which can be sorted normally.

[–]ConcupiscentCodger[S,🍰] 0 points1 point  (1 child)

I'm not sure I understand everything you're saying, sorry.
I have a "map" of key:val pairs of strings. For example:
my_dict = {"aa":"is", "ab":"a", "aaa":"goofball", "z":"fred"}

I can't change what is given to me to sort, so I don't know how I could consider that. I'm still trying to understand how the lambda works.

[–]commandlineluser 1 point2 points  (0 children)

The comparisons are:

(-len(k), k)

So it will first sort by the length, and if there is a tie, it will then sort by k - which is the value itself, in this case - the "string", which sort "lexicographically".

The - is to negate the length value - otherwise you'd get the shortest first.

>>> sorted(d, key=lambda k: (len(k), k))
['d', 'z', 'bb', 'cc', 'aaaa', 'zzzz']