4

How to sort a list of strings so that the number of capitals beginning the string is the main criterion?

What I have:

names = ["JOE", "Kate", "WILfried", "alfred", "denis"]

print sorted(names)

>>> ['JOE', 'Kate', 'WILfried', 'alfred', 'denis']

What I would like:

>>> ['JOE', 'WILfried', 'Kate', 'alfred', 'denis']

EDIT

In other words, I would like:

  • in first positions, sorted strings beginning with n capitals
  • then, sorted strings beginning with n-1 capitals
  • " " " " " " " " " " " " " " " " " " " " " " " " " n-2 " " " " " "
  • etc.

(Capitals following at least one lowercased character doesn't matter.)

taalf
  • 179
  • 1
  • 10
  • What do you mean? JustinB – Rajan Chauhan Aug 18 '17 at 17:03
  • Related: https://stackoverflow.com/questions/8966538/syntax-behind-sortedkey-lambda – bendl Aug 18 '17 at 17:03
  • 1
    Surely "Kate" should come before "WILFRIED"? If not, please explain your sorting rules more carefully. – Adi219 Aug 18 '17 at 17:04
  • Possible duplicate of [Syntax behind sorted(key=lambda :)](https://stackoverflow.com/questions/8966538/syntax-behind-sortedkey-lambda) – Tague Griffith Aug 18 '17 at 17:06
  • Adi C: The default sorting puts 'Kate' between 'JOE' and 'WILfried, but I would like strings with more capitals at the first positions. – taalf Aug 18 '17 at 17:30
  • @taalf I've put the question on hold - you've got requirements spattered about in comments that you're not clarifying. Please take the time to [edit] your question to clarify exactly what it is you're after. Answers are descending into guessing now. – Jon Clements Aug 18 '17 at 18:11
  • @taalf Check out my answer hope it satisfies what you wanted – Rajan Chauhan Aug 18 '17 at 18:12

3 Answers3

4

Following function satisfies the requirement

l = ['JOE', 'Kate', 'WILfried', 'alfred', 'denis']
def k(x):
  for i,j in enumerate(x):
    if j.islower():
      return (-i,x)
  return (-len(x),x)

print(sorted(l,key=k))

This gives following output:

['JOE', 'WILfried', 'Kate', 'alfred', 'denis']

The function k gives weight to the number of uppercases appearing at the start of the string.
EDIT: Thanks @jdeseha for edits

Rajan Chauhan
  • 1,378
  • 1
  • 13
  • 32
  • I'd use `not j.isupper()` (in case there are numbers or symbols) and just put the `return` after the `for` (note that your function may return `None` if, for example, the argument ends with a number. Also the cast to float doesn't seem necessary. – jdehesa Aug 18 '17 at 18:13
  • 1
    it just works :) thank you very much :) – taalf Aug 18 '17 at 21:09
3

You can use :

print(sorted(names,key = lambda x: (not x.isupper(), x)))
>>> ['JOE', 'WILFRIED', 'Kate', 'alfred', 'denis']

UPDATED :

...

PRMoureu
  • 12,817
  • 6
  • 38
  • 48
2

Here is a (somewhat ugly) solution:

names = ['JOE', 'Kate', 'WILfried', 'alfred', 'denis']
k = lambda s: ([-i for i, u in enumerate(map(str.isupper, s)) if not u] or [-len(s)],
               s.lower())
print sorted(names, key=k)
>>> ['JOE', 'WILfried', 'Kate', 'denis', 'alfred']

Maybe you want to put that lambda as a proper function.

jdehesa
  • 58,456
  • 7
  • 77
  • 121
  • Fixed now (hopefully!) – jdehesa Aug 18 '17 at 17:50
  • Hmm, just realizing, the order this yields is: first every all-caps string, then order by number of initial number of caps (until no initial caps). Not sure if that is alright or instead it should be ordered by initial number of caps only. – jdehesa Aug 18 '17 at 17:54
  • Do somehow what I need. Very appreciate the effort. Will adapt it in case I meet a situation not handled but this is a very good start. Thanks! – taalf Aug 18 '17 at 20:59
  • Ah... the Rajan's answer respects the number of caps... So I will validate this one. Nevertheless, thank you for your help :) – taalf Aug 18 '17 at 21:06
  • @taalf Yep, I've made a small edit to emulate the same behavior... It's about the same as Rajan's answer, although his is probably clearer (and, in theory, slightly more efficient, since it stops at the first non-uppercase of every string). – jdehesa Aug 19 '17 at 12:24