Object property-names in JavaScript must be strings. This may seem obvious, but it has serious repercussions if you’re used to the free-wheeling style of Ruby’s Hash.


In order to have the flexibility I’m used to, I whipped up this simple Hash that stores both keys and values in separate arrays.

/* JavaScript Associative Array */'string''length'// TODO: Need to do something smarter for objects

span class="st0">'th' :
      { 1 => 'st', 2 => 'nd', 3 => 'rd' }.fetch(n % 10, 'th'

This code is based on a snippet for generating ordinals. The other examples used a fixed-size array for pulling suffixes. Instead, I use the 2-parameter version of Hash#fetch to supply a default for most numbers.

It is frequently desirable to represent bidirectional relationships in web applications, and simple databases are not immediately suitable for this information.

Let’s start with a simple unidirectional example that a database like MySQL handles well.

User 1 wants to be friends with User 2.

A row is inserted into the befriendings table with User 1 as the initiator and User 2 as the recipient.

Now, if your friendships are unidirectional the job is pretty much done. If User 2 wants to be friends with User 1, they can initiate a befriending of their own. On Kongregate, users you have befriended are called “Friends”, and users who have befriended you are called “Fans”


However, sometimes you want to follow a friendship model more like the one used by Facebook or MySpace, where all friendships are bidirectional.

Both of these sites implement a relationship-confirmation step before any relationship is finalized. For simplicity’s sake, I’ll skip over modeling that step, but suffice it to say, pending relationships are best stored separately from confirmed relationships. Attempting to mix the two needlessly mingles data and complicates application logic.

Let’s assume any user has the ability to initiate the bidirectional relationship. Now, in the befriendings table, we technically have all the information necessary to list all friends of User 1, and all friends of User 2.



In the first case, only one index can be used (on either initiatior_id, or on recipient_id), and in both cases, the ORDER clause necessitates a filesort on the results.

So the simplest approach here would be to simply duplicate the relationship data in another row, swapping the initiator and recipient ids. This is okay, but starts to fall apart as store more information about the relationship itself. For example, imagine we store a compatibility score between the two users, as last.fm does.

This information would need to be duplicated, as well as keeping the the various timestamps in-sync. Indices against these fields would bloat unnecessarily.

Instead, we can move the duplicated information to a dedicated table, eliminating indices on the relationships table itself. The column by which results are ordered is moved from the relationships table to the dedicated table.


Users then access their list of friends through a :friends association and can access the rich information about the relationship itself through the :befriendings association. The befriending_edges table’s indices are simple and very efficient, and looking up befriendings uses a single primary key, and looking up friends uses two primary keys.


Despite an unfortunate fainting spell at the end of the presentation, overall it went alright.

The slides are available here.