I am trying to sort an list which contains numbers and letters:
names = ["5aG", "6bG", "10cG", "J1", ...]
The output should look like this:
['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR','10aG', '10bG', '10cG', '10aR', 'J1', 'J2']
The first element of the string is always a number from 5 - 10, then there is a letter from a - c and in the end there is another letter ("G" or "R").
Moreover there are the strings "J1" and "J2". They should be always the last ones ("J1" before "J2").
How can I achieve something like that? I thought about using a lambda function.
So far I hard coded it, but I think there should be a better solution.
This is my hard coded version:
classes = ['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR','10aG', '10bG', '10cG', '10aR', 'J1', 'J2']
def s(v):
"""Get index of element in list"""
try:
return classes.index(v)
except ValueError:
return 500
l = ['5bG', '6aG', '6bG', '8aR', '9aG', '9bG', '9aR', '10cG', '10aR', 'J1', 'J2', '5aG', '']
w = sorted( l, key=s)
print(w)
You can use re
to extract the front integer, then rely on tuple
comparison.
import re
def key(s):
num, letters = re.match(r'(\d*)(.*)', s).groups()
return float(num or 'inf'), letters
sorted_names = sorted(names, key=key)
Note how you can rely on float('inf')
to have your tokens without prefix digits pushed to the end.
You can you try this:
After scrambling your desired output:
import re
s = ['5aR', '7aR', '10aR', '10cG', '9bG', '8aR', '8bG', '6bR', '5aG', '9aG', 'J1', '6aR', '6aG', '5bR', '7aG', '7bG', '9aR', '5bG', 'J2', '6bG', '10bG', '8aG', '10aG', '6cG']
c, d, *h = sorted(s, key=lambda x:[False if not x[0].isdigit() else int(re.findall('^\d+', x)[0]), x[-1], x[-2]])
sorted_result = [*h, c, d]
Output:
['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR', '10aG', '10bG', '10cG', '10aR', 'J1', 'J2']
Here is one way.
lst = ['7aR', '9aG', '7bG', '10cG', '5bG', '6aG', '6bG', '10bG', 'J2', '5aR', '10aG', '9bG', '6aR', '7aG', '10aR', '9aR', '8aR', 'J1', '5bR', '6bR', '5aG', '8bG', '6cG', '8aG']
sorted([i for i in lst if i[0]!='J'], key=lambda x: [int(x[:-2]), x[-1], x[-2]]) + \
sorted(i for i in lst if i[0]=='J')
# ['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR', '10aG', '10bG', '10cG', '10aR', 'J1', 'J2']
The easiest way to do this is to use Python's built-in sorting functions. By providing a suitable function as the key
argument you can sort things in just about any order you choose.
Internally, when you provide a key function the sort generates a list of two-element tuples. The first element of the tuple is a sort key, the result of applying the key function to the second element, the value from the list. It then sorts those tuples, and returns a list of the second elements. This is known as decorate-sort-undecorate.
Most of your strings are an integer followed by two letters. The remainder, which you wish to appear last, are either "J1"
or "J2"
. The following should be a suitable key function. I take the precaution of applying the int
function to the numbers, to ensure that they sort numerically rather than lexicographically (because '2' > '10'
).
def key_func(s):
# Ensure J-strings are at the end
if s.startswith('J'):
return (1000000, 'J', int(s[1:]))
else:
# The rest, split into digits and two characters
return (int(s[:-2]), s[-2], s[-1])
When tested with a randomised copy of your data the result of
data = ['8aG', '5aR', '6aG', '10aG', '6cG', '8bG', '9aG',
'5aG', '6bG', '7aR', 'J1', '10cG', '10bG', '10aR',
'6bR', 'J2', '6aR', '8aR', '7aG', '9aR', '5bR',
'9bG', '7bG', '5bG']
print(sorted(data, key=key_func))
appears to be correct (line breaks inserted for readability):
['5aG', '5aR', '5bG', '5bR', '6aG', '6aR', '6bG', '6bR',
'6cG', '7aG', '7aR', '7bG', '8aG', '8aR', '8bG', '9aG',
'9aR', '9bG', '10aG', '10aR', '10bG', '10cG', 'J1', 'J2']
With custom compound_sort()
function:
import re
lst = ['9bG', '9aR', 'J2', '7bG', '7aG', '6bR', 'J1', '6cG', '6aG', '6bG', '5bG', '5aG', '8bG', '5bR', '8aR', '5aR', '10aR', '6aR', '10bG', '10aG', '9aG', '10cG', '7aR', '8aG']
pat = re.compile(r'(\d+)(.*)|(J)(\d+)')
def compound_sort(t):
t = tuple(filter(None, t)) # filter empty(None) matches
return (int(t[0]),) + t[1:] if t[0] != 'J' else (float('inf'), t[1])
result = sorted(lst, key=lambda x: compound_sort(pat.search(x).groups()))
print(result)
The output:
['5aG', '5aR', '5bG', '5bR', '6aG', '6aR', '6bG', '6bR', '6cG', '7aG', '7aR', '7bG', '8aG', '8aR', '8bG', '9aG', '9aR', '9bG', '10aG', '10aR', '10bG', '10cG', 'J1', 'J2']
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.