When sorting by multiple keys, I need the second key to alternate between standard and reverse sorting. This is to minimize the change in the second key. For example, if I have a list of dictionaries, I want the first sort (group key) to be sorted and then the second sort (sort key) to alternate. This should produce a sort like this:
[{'group': 'a', 'sort': 1},
{'group': 'a', 'sort': 2},
{'group': 'a', 'sort': 2},
{'group': 'b', 'sort': 2},
{'group': 'b', 'sort': 1},
{'group': 'b', 'sort': 1},
{'group': 'c', 'sort': 1},
{'group': 'c', 'sort': 1},
{'group': 'c', 'sort': 2}]
That limits the change in the field called "sort" as it alternates each time as the "group" changes.
I have looked around and did not see a standard way to accomplish this without rolling my own. I think the code is fairly straight forward but curious if I might be missing something or an easier/faster way.
Any comments would be appreciated.
#!/usr/bin/env python3
""" Misc sort routines """
from operator import itemgetter
import pprint
def alternate_sort(data, group_key, sort_key,
group_reverse=False, sort_reverse=False):
""" Sort data within groups while alternating asc/desc on sort key. """
partitions = [] # Tuples of start/end indexes per group
last_key = None # Remember when groups change
group_start = 0 # Remember start index of group
final_sort = [] # The final sorted listing
# Make sure the data is sorted by group
data = sorted(data, key=itemgetter(group_key), reverse=group_reverse)
# Gather the group partitions
for i, g in enumerate(data):
# When the group changes
if last_key != g[group_key]:
# End the previous group partition if there is one
if i > 0:
partitions.append((group_start, i))
# Start a new group partition
group_start = i
last_key = g[group_key]
# Close the last group partition
partitions.append((group_start, len(data)))
# Go through the partitions, alternate the sort each time using sort key.
for i, p in enumerate(partitions):
part = data[p[0]: p[1]]
if i % 2 == 0:
final_sort += sorted(
part, key=itemgetter(sort_key), reverse=sort_reverse)
else:
final_sort += sorted(
part, key=itemgetter(sort_key), reverse=not sort_reverse)
return final_sort
if __name__ == "__main__":
unsorted_data = [
{"group": "a", "sort" : 1},
{"group": "a", "sort" : 2},
{"group": "b", "sort" : 2},
{"group": "b", "sort" : 1},
{"group": "c", "sort" : 1},
{"group": "a", "sort" : 2},
{"group": "c", "sort" : 2},
{"group": "b", "sort" : 1},
{"group": "c", "sort" : 1}
]
sorted_data = alternate_sort(unsorted_data, "group", "sort",
group_reverse=True, sort_reverse=True)
pprint.pprint(sorted_data)
sorted_data = alternate_sort(unsorted_data, "group", "sort")
pprint.pprint(sorted_data)