Showing posts with label zen and the art of data structure maintenance. Show all posts
Showing posts with label zen and the art of data structure maintenance. Show all posts

Sunday, September 23, 2007

Zen Python: The Sound of One Record Appending

Recently, I tried to make a Python program ultra-terse, as a fun puzzle.

I re-discovered two things:

  • I love Python. I hadn't used it in years and yet I was "back in gear" within a few minutes.
  • Python just seems to work. It is like a substance that can be a solid or a liquid, depending on your requirements, and at the same time.
In particular, I found that Python V 2.5 has some pretty cool ways of handling empty maps, or 'dictionaries' (in the Python parlance). This is elegant stuff, and yet easy enough that you need not know Python.

Consider a problem where we want to organize participants in a triathlon: a person has a name, gender, and age. Age groups will be 20s, 30s, 40s, etc.

By using a lambda expression (think anonymous function), on a defaultdict method, we can specify the default behaviour of a dictionary when there is no entry for the key.

myEasyMap = collections.defaultdict (lambda:[])

In this case, myEasyMap will create an empty list and insert it, when a key is not found. For example:

myEasyMap = collections.defaultdict (lambda:[])
myEasyMap[30].append("John Smith")

The above example appends "John Smith" into the list of triathletes in the 30s, even though there were not yet any listed.

The really cool thing is that we can chain these default dictionaries:

myData = collections.defaultdict (lambda: collections.defaultdict (lambda:[]))

myData["M"][30].append("John Smith")
myData["F"][20].append("Barbara Doe")

Above, myData is a map that resolves to a map of lists. There are two outer keys ("M" and "F"); the inner keys are the age ranges.

Should we do this in production? Probably not. But it is gorgeous.

Below, we iterate over some records and organize athletes:

myData = collections.defaultdict (lambda: collections.defaultdict (lambda:[]))

records = []
records.append( { 'name': "John Smith", 'age': 35, 'gender': "M" } )
records.append( { 'name': "Axl Rose", 'age': 45, 'gender': "M" } )
records.append( { 'name': "Britney Doe", 'age': 25, 'gender': "F" } )

for record in records:
name = record['name']
age = record['age'] % 10
gender = record['gender']
myData[ gender ][ age ].append( name )

Neato with a capital 'O'.

Ruby has similar behaviour, which is not surprising (see Porsches and Ferraris) but I don't know many languages that can do this. (e.g. I don't know of a way to do it in Groovy. Do you, Groovy readers?)