Reverse sorting on arbitrary key segments

I have a database-y requirement to sort a list by a series of keys which are user-defined. Each of the keys could be a forward or a reverse sort.

In essence I have a list of dictionaries:

rows = [
  dict (name="John", age=30, id=123),
  dict (name="John", age=40, id=456),
  dict (name="Fred", age=20, id=567),
]

and a set of keys from the user:

keys = [
  ("name", "asc"),
  ("age", "desc")
]

and I want to call sorted with a key function to end up with:

[
  dict (name="Fred", age=20, id=567),
  dict (name="John", age=40, id=456),
  dict (name="John", age=30, id=123),
]

Obviously, I can’t just use reversed=True since that would reverse all or nothing. The main sorting page on the wiki rather oddly suggests that you sort against the first column and then against the second.

Which would seem to leave you with (ie age order descending):

[
  dict (name="John", age=40, id=456),
  dict (name="John", age=30, id=123),
  dict (name="Fred", age=20, id=567),
]

[Update: As several people have pointed out, the page actually suggests sorting against the second column reversed and then against the first, relying on the fact that Python’s sort is stable. But meanwhile, granted my misunderstood premise…]

The sorting-lists-of-dictionaries page comes closer, but assumes that all the keys are numeric.

What I’ve actually done is to create a Reversed class which simply reverses the sense of calling __lt__:

class Reversed (object):
  def __init__ (self, value):
    self.value = value
  def __lt__ (self, other):
    return self.value > other.value

and then to implement a key function like this:

def sorter (row):
  return tuple (
    (row[col] if dir=="asc" else Reversed (row[col])) 
      for (col, dir) in keys
  )

and sorted:

sorted (rows, key=sorter)

Now, all this certainly works, but is it a sane way to achieve the end? I was convinced that there should be something fancy I could do with a key function alone, avoiding an auxiliary class, but since the column values could be any datatype, including user-defined ones, I can’t just use the usual -number or -timedelta tricks.

If it looks like this is a useful technique, I’ll add it to the sorting wiki page. But I thought it best to subject it public scrutiny first. To many eyeballs… etc. etc.

Comments

Comments powered by Disqus