Lately, I’ve had the need to sort ActiveRecords by a certain attribute, but the order is completely arbitrary, and supplied by the user.
Take, for example, the following call to a model in Rails:
input = %w(MN CO TX CA)
State.find_by_state_code(input)
This generates SQL that looks something like:
SELECT * FROM states WHERE state_code IN('MN','CO','TX', 'CA')
state_code
----------
CA
CO
MN
TX
Seemingly random results (actually alphabetized, but still worthless to me), but I have a specific requirement to process the results in the order the user specified (MN CO TX CA).
We can solve this by using Array#assoc to build a simple index of States and their codes:
input = %w(MN CO TX CA) # user-supplied
states = State.find_by_state_code(input)
states.map! { |state| [state.state_code, state] }
# states now looks something like:
# [["CA", #<State state_code="CA">], ["CO", #<State state_code="CO">], ["MN", #<State state_code="MN">], ["TX", #<State state_code="TX">]]
Now we iterate over the user-supplied list, and use assoc to pluck out the state that we want:
input.each { |s| puts states.assoc(s).last.inspect }
#<State state_code="CA" ... >
#<State state_code="CO" ... >
#<State state_code="MN" ... >
#<State state_code="TX" ... >
Previously, I was doing some ugly mumbo-jumbo with creating a hash, but assoc is a much cleaner solution. This also allows me to stick to using the SQL IN() operator, instead of iterating over the user input and doing a single call to the database, which obviously gets expensive.
This sort of reminds me of a Schwartzian Transform, although it’s not exactly the same.