简体   繁体   中英

Sort list of strings in natural fashion

I have the following:

sorted( ("A","a","b","B","CC","c"), key=lambda x: x.lower() )

Which gives:

['A', 'a', 'b', 'B', 'c', 'CC']

How do I sort it like this:

['a', 'A', 'b', 'B', 'c', 'CC']

where if there are two values the same, the lower form comes first - a before A.

I'm guessing I do something like this:

sorted( ("A","a","b","B","CC","c"), key=lambda x: (x.lower(),x) )

but this gives A before a:

['A', 'a', 'B', 'b', 'c', 'CC']

How do I do the opposite?

UPDATE

To clarify things further, the following strings: ["A","a","aA","aa"] should be sorted as:

["a","A","aa","aA"]

So "aa" before "aA" and "aaA" before "aAa" etc.

So:

lst = ["A","aA","aa","aaa","aAa","aaA","b","B","CC","c"]

should sort to:

['A', 'aa', 'aA', 'aaa', 'aaA', 'aAa', 'b', 'B', 'c', 'CC']

You could do:

lst = ["A","a","b","B","CC","c"]

result = sorted(lst, key= lambda x: (x.lower(), not x.islower()))
print(result)

Output

['a', 'A', 'b', 'B', 'c', 'CC']

UPDATE

Given the new you examples you could use the following key:

lst = ["A", "aA", "aa", "aaa", "aAa", "aaA", "b", "B", "CC", "c"]
result = sorted(lst, key=lambda x: (x.lower(), ''.join(str(c.isupper()) for c in x)))
print(result)

Output

['A', 'aa', 'aA', 'aaa', 'aaA', 'aAa', 'b', 'B', 'c', 'CC']

For the other example ( ["A","a","aA","aa"] ) it gives:

['a', 'A', 'aa', 'aA']

Here is one way:

sorted( lst, key=lambda x: (x.lower(), *map(str.isupper, x)) )
#['A', 'aa', 'aA', 'aaa', 'aaA', 'aAa', 'b', 'B', 'c', 'CC']

First sort by case insensitive letter. Then call str.upper on all the characters of each string as the second sort condition. This will rank lower case letters first for the same length strings.

Python 2 Version

The above syntax only works in python3, but you can equivalently do the following in python2:

sorted( lst, key=lambda x: ((x.lower(),) + tuple(map(str.isupper, x))) )

You can do it this way:

>>> sorted( ("A","a","b","B","CC","c"), key=lambda x: (x.lower() + str(x != x.lower()) ,x) )
['a', 'A', 'b', 'B', 'c', 'CC']

It works by comparing the lowercase version of the letter to the actual letter. If there is a match on lower case then str(x != x.lower() will be 0 , otherwise 1 .

Here is a solution that works based on previous answers:

sorted(lst, key= lambda x: (x.lower(), [c.isupper() for c in x]))

and this also seems to work:

sorted(lst, key= lambda x: (x.lower(), x.swapcase()))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM