Reverse multiple string sorting via "key" function

I have a table selection, so a list of rows of column data. list[dict[str, str]]
The keys in each dictionary are just the column names that map to the row’s data.

Now in my app I want my users to be able to sort by any amount of columns with the ability to reverse some.

For example, imagine the following data:

data = [
    {'name': "Bob", 'age': 15, 'height': 140},
    {'name': "Bobby", 'age': 15, 'height': 178},
    {'name': "Claire", 'age': 20, 'height': 165},
]

Now the user wants to primarly sort by age and secondarly by name in reverse.

My current solution is something similar to this:

def get_sort_key(*columns: tuple[str, bool]):
    def key(row: dict[str, str]):
        tuple(
            tuple(
                ord(c) * (-1 if reverse else 1)
                for c in
                row[col]
            )
            for col, reverse in columns
        )
    
    return key

But I recently realized that in my case above, “Bob” would still by sorted before “Bobby”, because “Bob” is the same string as “Bobby” up to the point it ends and therefore “Bob” < “Bobby” in any order.

A solution would be to pad the codepoints with -1 (or +1 in reverse) for the maximum expected string length, but I feel like there should be a way easier solution for my problem.

EDIT:
Assume all values are stringified before the the data is put into a sorting function!

Rather than trying to convert strings to tuple of integers, you could instead do the comparison of the strings, and reverse the result if you want to. edit: Although this requires using a comparator rather than a key.

And how would I use a comparison for a key?

Ignore my last response, I figured it out! (with the cmp_to_key function)

I am actually surprised because I did a lot of similar stuff with sorting already but have not once realized that this function exists. Good to know and thank you for the help!!

Another approach that’s surprisingly (to me) underused: don’t try to do it all at once. Do multiple “simple” sorts instead, in a chain. This can often run faster than forcing use of (an expensive!) complex Python comparison function to do it all at once. And probably “a lot faster” than resorting to functools.cmp_to_key.

So try sorting first just by name, and pass reverse=True. Then sort the result of that again, but just by age. Because Python’s sort is stable, records with the same age will retain their incoming reverse-sorted-by-name order.

To make that very clear:

>>> from operator import itemgetter
>>> data = [
...     {'name': "Bob", 'age': 15, 'height': 140},
...     {'name': "Bobby", 'age': 15, 'height': 178},
...     {'name': "Claire", 'age': 20, 'height': 165},
... ]
>>> data.sort(key=itemgetter("name"), reverse=True)
>>> data.sort(key=itemgetter("age"))
>>> import pprint
>>> pprint.pprint(data)
[{'age': 15, 'height': 178, 'name': 'Bobby'},
 {'age': 15, 'height': 140, 'name': 'Bob'},
 {'age': 20, 'height': 165, 'name': 'Claire'}]