6

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']
martineau
  • 119,623
  • 25
  • 170
  • 301
Demosthene
  • 359
  • 1
  • 4
  • 16

3 Answers3

6

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']

UPDATE

@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']

UPDATE(final, i hope so)

@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.

vishes_shell
  • 22,409
  • 6
  • 71
  • 81
4

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)

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
2

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.)

Stefan Pochmann
  • 27,593
  • 8
  • 44
  • 107