I have the following list
l = ['SRATT', 'SRATW', 'CRAT', 'CRA0', 'SRBTT', 'SRBTW', 'SRAT0', 'SRBT0']
which I would like to sort alphabetically, with the added rule that a string containing a number at the end (actually always 0) must come after the last fully alphabetical string (last letter is at most W).
How can I do that? (using, if possible, a simple method like sorted
)
For this example list, the desired result would be
['CRAT', 'CRA0', 'SRATT', 'SRATW' , 'SRAT0', 'SRBTT', 'SRBTW', 'SRBT0']
e.g. the following doesn't work
sorted(l, key=lambda x: x[-1].isdigit())
since it puts the strings containing a final number at the end, like so
['SRATT', 'SRATW', 'CRAT', 'SRBTT', 'SRBTW', 'CRA0', 'SRAT0', 'SRBT0']
Alphanumeric ordering is done using the current language sort order on the client machine as defined by the operating system (i.e. Windows). The user requests the sort by clicking on the column header. A grid must have column headings in order to be sorted by the user.
you should split the strings in two first; the other part being the integer part, and the other the string part. then first compare the integers - if the integers are not equal, the string that should appear first is the one with the smaller integer part.
Using sort(), lamba, index(): The sort() function does the required in-place sorting(without creating a separate list to store the sorted order) along with a lambda function with a key to specify the function execution for each pair of tuples, the index() function helps to get the order from our custom list list_2.
You have to retain the alphabetic criterion on the string (minus the last element), and introduce the other criterion: ends by a digit.
sorted(l, key=lambda x: (x[:-1] ,x[-1].isdigit()))
a more complex but robust way:
sorted(l, key=lambda x: (x[:-1] if len(x)>1 and not x[-1].isdigit() else x,x[-1].isdigit() if x else False))
(fixed the corner case noted by Stefan where the list is made of 1 or 0 sized elements or the ['AB', 'AA']
case)
Working solution at the bottom!
First attempt:
>>> l = ['SRATT', 'SRATW', 'CRAT', 'CRA0', 'SRBTT', 'SRBTW', 'SRAT0', 'SRBT0']
>>> sorted(l, key=lambda x: (x[:-1], x[-1].isdigit()))
['CRAT', 'CRA0', 'SRATT', 'SRATW', 'SRAT0', 'SRBTT', 'SRBTW', 'SRBT0']
@StefanPochmann said that this will fail with same beginning and different last non-digit character.
We can add additional element in the end of key, which would be element itself
>>> l = ['SRATT', 'SRATW', 'CRAT', 'CRA0', 'SRBTT', 'SRBTW', 'SRAT0', 'SRBT0', 'B', 'A']
>>> sorted(l, key=lambda x: (x[:-1], x[-1].isdigit(), x))
^
additional element
['A', 'B', 'CRAT', 'CRA0', 'SRATT', 'SRATW', 'SRAT0', 'SRBTT', 'SRBTW', 'SRBT0']
@Demosthene noted that the second attempt is not working, and that's true
So working solution would be to pick any digit at the end of element(if it exist) and change to symbol that is out of letters and digits scope, e.g. '{'
:
sorted(l, key=lambda x: ''.join((x[:-1], '{')) if x[-1].isdigit() else x)
or
sorted(l, key=lambda x: x[:-1] + '{' if x[-1].isdigit() else x)
as @StefanPochmann noted. Which is probably faster.
Here's another simple way, just treat 0
as Z
:
>>> sorted(l, key=lambda x: x.replace('0', 'Z'))
['CRAT', 'CRA0', 'SRATT', 'SRATW', 'SRAT0', 'SRBTT', 'SRBTW', 'SRBT0']
(I'm assuming there are no zeros earlier in the string, let me know if that's wrong.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With