Reputation: 37
I came across a question on HackerRack website, which I solved using my own logic but someone had a much better solution which I am having a hard time to understand.
Question: Your task is to sort the string in the following manner: All sorted lowercase letters are ahead of uppercase letters. All sorted uppercase letters are ahead of digits. All sorted odd digits are ahead of sorted even digits.
Answer:
print(*sorted(input(), key=lambda c: (c.isdigit() - c.islower(), c in '02468', c)), sep='')
Can someone please explain the lambda function above? More specifically what the c.isdigit() - c.islower()
is doing?
Upvotes: 3
Views: 434
Reputation: 6234
a little bit about sorted()
built-in function that builds a new sorted list from an iterable
sorted()
have a key parameter to specify a function to be called on each list element before making comparisons.The value of the key parameter should be a function that takes a single argument and returns a key(in this particular case it is a tuple) to use for sorting purposes. This technique is fast because the key function is called exactly once for each input record.
let's break the sorted
function into a more readable piece of code.
def key_function(character):
print((character.isdigit() - character.islower(), character in "02468", character))
return (character.isdigit() - character.islower(), character in "02468", character)
input_string = "1949 Film Prejudice"
print(*sorted(input_string, key=key_function), sep="")
This is the intermediate list of tuple which I have contrived just for the sake of explanation of the problem.
[
(1, False, "1"),
(1, False, "9"),
(1, True, "4"),
(1, False, "9"),
(0, False, " "),
(0, False, "F"),
(-1, False, "i"),
(-1, False, "l"),
(-1, False, "m"),
(0, False, " "),
(0, False, "P"),
(-1, False, "r"),
(-1, False, "e"),
(-1, False, "j"),
(-1, False, "u"),
(-1, False, "d"),
(-1, False, "i"),
(-1, False, "c"),
(-1, False, "e"),
]
The comparison between these tuples is the key to understand how this works.
If you'll just sort this list(which uses tuple comparison to sort) you will get the desired result.
(1, False, "1") > (1, False, "9")
Integer comparison intuitive because it's just integers we have to deal with.
just to give the glimpse of what True was False used to be
PEP 285
(
True
andFalse
constants were added to the built-ins in Python 2.2.1, but the 2.2.1 versions are simply set to integer values of1
and0
and aren't a different type.)
so the comparison between them is also the same as between integers.
True - False
is 1
and False - True
is -1
.
These strings are being ordered by the ASCII values of their characters (p is 112 in ASCII and P is 80). Technically Python compares the Unicode code point (which is what ord does) for these characters and that happens to be the same as the ASCII value for ASCII characters.
so ultimately it also boils down to integer comparison.
Hopefully, now you'll have a good understanding of what is happening in that sorted
function :)
https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/
Upvotes: 2
Reputation: 31609
If I try to substract two booleans, it will give me an integer since booleans are subclasses of int
:
>>> True - True
0
>>> False - False
0
>>> False - True
-1
In your case, the first boolean correspond to the result of c.isdigit()
and the second one to c.islower()
.
So:
>>> True - True # ex: '7' -> isdigit(): True, islower(): True -> 1 - 1 = 0
0
>>> False - False # ex: 'B' -> isdigit(): False, islower(): False -> 0 - 0 = 0
0
>>> False - True # ex: 'g' -> isdigit(): False, islower(): True -> 0 - 1 = -1
-1
Every item in the tuple will be used to check order. Starting from the first item, and going to the 2nd and 3rd in case of ties.
Knowing that booleans can be compared to each other ((True > False) is True
) and that strings can be too ('abc' < 'xyz'
), you can have the following order of tuples:
(-1, 0, 'g') < (0, 0, 'B') < (0, 0, '7') < (0, 1, '6') < (0, 1, '8')
Then, since sorted
does sort in increasing order by default, you get what you were expecting: sorted lowercase letters ahead of sorted uppercase letters ahead of sorted odd digits ahead of sorted even digits.
Upvotes: 2
Reputation:
The key here is a tuple and Python will determine the sorted position of elements on the bases of the first element of key to satisfy a unique position i.e. a comparison resulting in inequality. In this case the tuple key is assigning different key to each value.
For example: c='1234ABab'
, now for the 0th element '1' the isdigit()
will return True being casted into 1 and islower()
to 0 hence the first element of the key will be 1 second will be 0 and third will be '1' itself. Hence the key for this element is (1,0,'1')
and this is the case for all odd nums. For evens the key will be (1,1,'2')
, for lowers (-1,0,'a')
and for capitals (0,0,'A')
. This way sorting will be done sequentially satisfying the key elements.
For more, you can search for tuple keys in sorting.
Upvotes: 1