If input is like ['z','t','Z','a','b','A','d'],then after sorting I want to get output like ['a','A','b','d','t','z','Z'] or ['A','a','b','d','t','Z','z'].
 
    
    - 107,652
- 25
- 181
- 264
 
    
    - 33
- 4
- 
                    Possible duplicate of [case-insensitive list sorting, without lowercasing the result?](https://stackoverflow.com/questions/10269701/case-insensitive-list-sorting-without-lowercasing-the-result) – Mad Physicist Jul 25 '19 at 19:18
- 
                    1Do you want order of upper-case and lowercase to be consistent, or stable based on the input list? – Mad Physicist Jul 25 '19 at 19:20
- 
                    I've posted an answer that explicitly addresses both options. – Mad Physicist Jul 25 '19 at 20:00
3 Answers
This will sort always upper-case letter first:
lst = ['z','t','Z','a','b','A','d']
print(sorted(lst, key=lambda k: 2*ord(k.lower()) + k.islower()))
Prints:
['A', 'a', 'b', 'd', 't', 'Z', 'z']
EDIT Thanks to @MadPhysicist in the comments, another variant:
print(sorted(lst, key=lambda k: (k.lower(), k.islower())))
 
    
    - 168,389
- 15
- 48
- 91
- 
                    2
- 
                    
- 
                    
- 
                    @AndrejKesely OP specifically stated that `['a','A','b','d','t','z','Z']` is valid output – DeepSpace Jul 25 '19 at 19:25
- 
                    Could be easily changed (if that matters) to lower first by switching to `isupper()` – Tomerikoo Jul 25 '19 at 19:26
- 
                    1@DeepSpace `sorted(['z','t','Z','A','b','a','d'], key=str.lower)` produces `['A', 'a', 'b', 'd', 't', 'z', 'Z']` – Andrej Kesely Jul 25 '19 at 19:27
- 
                    @AndrejKesely Indeed, I guess that's a side-effect of `sorted` using a stable version of quicksort. – DeepSpace Jul 25 '19 at 19:30
- 
                    
You could use sorted's (or list.sort's) extra keyword - key. You can pass to key a function according to which the sort will be performed. So for  example:
l = ['z','t','Z','a','b','A','d']
print(sorted(l, key=str.lower))
Gives:
['a', 'A', 'b', 'd', 't', 'z', 'Z']
Note: this will not preserve the order of lower/upper between different letters. It will preserve the order of original input.
 
    
    - 18,379
- 16
- 47
- 61
- 
                    1Bonus: If you are defining a lambda just to call a function on the object that is passed in, you can just use the static version, in this case for example `(sorted(l, key=str.lower))` – DeepSpace Jul 25 '19 at 19:27
- 
                    1@DeepSpace You are right. I wasn't sure if it can work here. I know that for example I could use just `int`, but since this is an instance method I wan't sure – Tomerikoo Jul 25 '19 at 19:28
- 
                    1@Tomerikoo. That's the beauty of methods in python. They are just regular functions that accept the instance as the first argument, and live in a class'es namespace. `str.lower(somestring)` produces identical results to `somestring.lower()`, if `somestring` is an instance of `str`. – Mad Physicist Jul 25 '19 at 20:02
- 
                    Yep, played with it and got it better now. Thanks a lot for the point-out and explanation! – Tomerikoo Jul 25 '19 at 20:04
There are two options on how this sorting could be done. Option 1 is stable, meaning that the order of elements is preserved regardless of case:
['A', 'b', 'a', 'B'] -> ['A', 'a', 'b', 'B']
The other option is to always put uppercase before or after lowercase:
['A', 'b', 'a', 'B'] -> ['A', 'a', 'B', 'b'] or ['a', 'A', 'b', 'B']
Both are possible with the key argument to list.sort (or the builtin sorted).
A stable sort is simply:
['A', 'b', 'a', 'B'].sort(key=str.lower)
A fully ordered sort requires you to check the original status of the letter, in addition to comparing the lowercased values:
['A', 'b', 'a', 'B'].sort(key=lambda x: (x.lower(), x.islower()))
This uses the fact that a tuples are compared lexicographically, or element-by-element. The first difference determines the order. If two letters have different values for x.lower(), they will be sorted as usual. If they have the same lowercase representation, x.islower() will be compared. Since uppercase letters will return 0 and lowercase letters return 1, lowercase letters will come after uppercase. To switch that, invert the sense of the comparison:
['A', 'b', 'a', 'B'].sort(key=lambda x: (x.lower(), not x.islower()))
OR
['A', 'b', 'a', 'B'].sort(key=lambda x: (x.lower(), x.isupper()))
OR
['A', 'b', 'a', 'B'].sort(key=lambda x: (x.lower(), -x.islower()))
etc...
 
    
    - 107,652
- 25
- 181
- 264