Let's say I have this list:
my_list = [
{'id': 'Bear@1.0.1'}, {'id': 'Bear@5.5.1'}, {'id': 'Bear@10.0.9'}, {'id': 'Bear@5.1'},
{'id': 'Air@7.6'}, {'id': 'Air@8.7'}, {'id': 'Air@10.0.0'}, {'id': 'Air@1.0'}
]
I want the resulting list to be sorted first by what precedes the @ and then by the version numbers.
Doing this: natsort.natsorted(my_list, key=itemgetter(*['id']), reverse=True)
I get the following result:
[{'id': 'Bear@10.0.9'},
{'id': 'Bear@5.5.1'},
{'id': 'Bear@5.1'},
{'id': 'Bear@1.0.1'},
{'id': 'Air@10.0.0'},
{'id': 'Air@8.7'},
{'id': 'Air@7.6'},
{'id': 'Air@1.0'}]
How do I get natsort to work so that all the items for "Air" come before all the items for "Bear", while still sorting also by version (as it's currently doing correctly)?
I would use a custom comparison function to reverse the comparison only on the version numbers, using natsort.natsort_keygen
to make a function that will compare versions numbers correctly.
from operator import methodcaller, itemgetter
my_list = [
{'id': 'Bear@1.0.1'}, {'id': 'Bear@5.5.1'}, {'id': 'Bear@10.0.9'}, {'id': 'Bear@5.1'},
{'id': 'Air@7.6'}, {'id': 'Air@8.7'}, {'id': 'Air@10.0.0'}, {'id': 'Air@1.0'}
]
vkey = natsort.natsort_keygen()
# Python 2's three-way comparison function:
# x < y returns negative
# x == y returns zero
# x > y returns positive
#
# To reverse the comparison, swap the arguments.
def cmp(x, y):
return -1 if x < y else 0 if x == y else 1
def cmp_str(x, y):
splitter = operator.methodcaller('split', '@')
getter = operator.itemgetter('id')
x1, x2 = splitter(getter(x))
y1, y2 = splitter(getter(y))
return cmp(x1, y1) \
or cmp(vkey(y2), vkey(x2))
for x in sorted(my_list, key=functools.cmp_to_key(cmp_str)):
print(x)
Output:
{'id': 'Air@10.0.0'}
{'id': 'Air@8.7'}
{'id': 'Air@7.6'}
{'id': 'Air@1.0'}
{'id': 'Bear@10.0.9'}
{'id': 'Bear@5.5.1'}
{'id': 'Bear@5.1'}
{'id': 'Bear@1.0.1'}