简体   繁体   中英

A more pythonic way to write this expression?

I'm supposed to take a list of words and sort it, except I need to group all Strings that begin with 'x' first.

Here's what I got:

list_1 = []
list_2 = []

for word in words:
  list_1.append(word) if word[0] == 'x' else list_2.append(word)

return sorted(list_1) + sorted(list_2)

But I have a feeling there is a much more elegant way to do this...

EDIT

Example: ['mix', 'xyz', 'apple', 'xanadu', 'aardvark'] yields ['xanadu', 'xyz', 'aardvark', 'apple', 'mix'] .

>>> words = ['xoo', 'dsd', 'xdd']
>>> sorted(words, key=lambda x: (x[0] != 'x', x))
['xdd', 'xoo', 'dsd']

Explanation: the key function returns a pair (tuple). The first element is False or True , depending on whether the first char in the string is 'x' . False sorts before True , so strings starting with 'x' will be first in the sorted output. The second element in the tuple will be used to compare two elements that are the same in the first element, so all the strings starting with 'x' will be sorted amongst themselves, and all the strings not starting with 'x' will be sorted amongst themselves.

First: stop saying "pythonic" when you mean "clean". It's just a cheesy buzzword.

Don't use terniary expressions like that; it's meant to be used as part of an expression, not as flow control. This is cleaner:

for word in words:
    if word[0] == 'x':
        list_1.append(word)
    else:
        list_2.append(word)

You can improve it a bit more--using terniary expressions like this is fine:

for word in words:
    target = list_1 if word[0] == 'x' else list_2
    target.append(word)

If words is a container and not an iterator, you could use:

list_1 = [word for word in words if word[0] == 'x']
list_2 = [word for word in words if word[0] != 'x']

Finally, we can scrap the whole thing, and instead use two sorts:

result = sorted(words)
result = sorted(result, key=lambda word: word[0] != 'x')

which first sorts normally, then uses the stable property of Python sorts to move words beginning with "x" to the front without otherwise changing the ordering.

words = ['xoo', 'dsd', 'xdd']
list1 = [word for word in words if word[0] == 'x']
list2 = [word for word in words if word[0] != 'x']

It should be noted that sorted was added in Python 2.4 . If you would like a shorter version which is a bit cleaner and somewhat more backwards compatible you can alternatively use the .sort() functionality directly off of list . It should also be noted that empty strings will throw an exception when using x[0] style array indexing syntax in this case (as many examples have). .startswith() should be used instead, as is properly used in Tony Veijalainen's answer .

>>> words = ['mix', 'xyz', '', 'apple', 'xanadu', 'aardvark']
>>> words.sort(key=lambda x: (not x.startswith('x'), x))
>>> words
['xanadu', 'xyz', '', 'aardvark', 'apple', 'mix']

The only disadvantage is that you're mutating the given object. This may be remedied by slicing the list beforehand.

>>> words = ['mix', 'xyz', '', 'apple', 'xanadu', 'aardvark']
>>> new_words = words[:]
>>> new_words.sort(key=lambda x: (not x.startswith('x'), x))
>>> new_words
['xanadu', 'xyz', '', 'aardvark', 'apple', 'mix']
>>> words
['mix', 'xyz', '', 'apple', 'xanadu', 'aardvark']
words = ['xoo', 'dsd', 'xdd']
list1=filter(lambda word:word[0]=='x',words)
list2=filter(lambda word:word[0]!='x',words)

To resend variation SilenGhosts code (feel free to copy, SilentGhost) as code not command prompt log

notinorder = ['mix', 'xyz', '', 'apple', 'xanadu', 'aardvark']
print sorted(notinorder, key = lambda x: (not x.startswith('x'), x))
>>> x = ['abc', 'xyz', 'bcd', 'xabc']
>>> y = [ele for ele in x if ele.startswith('x')]
>>> y
['xyz', 'xabc']
>>> z = [ele for ele in x if not ele.startswith('x')]
>>> z
['abc', 'bcd']

More along the lines of your original solution:

l1=[]
l2=[]
for w in sorted(words):
    (l1 if w[0] == 'x' else l2).append(w)
l1.extend(l2)
return l1

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