coderrr

July 25, 2009

More Ordered Hashes for Ruby 1.8

Filed under: ruby — Tags: — coderrr @ 10:59 am

Here’s one more ParseTree based technique for ordered hashes, it’ll be the last one, I promise. This one doesn’t require any Ruby2Ruby string building/evaling craziness. The syntax is the same as the previous ParseTree example:

hash = H{{ :key1 => value1, 'key2' => value2 }}

But with this implementation you can only use literals as keys, meaning strings, symbols, and numbers.

First we call the block with yield to get the original unordered hash. Next we use ParseTree just to find the keys of the hash in order. Since we only support literals we can use the value straight out of the s-expression, no conversion is needed. And we don’t need to look at the keys’ values in the s-expression since we already have them in the unordered hash. Finally, we create a new ordered hash and set each key in order, grabbing the value from the original (unordered) hash. This allows us to simplify the use of the s-expressions from ParseTree and not have to deal with code generation at all (Ruby2Ruby). It also gives about a 4x speedup over the previous technique.

Here’s the code:

class K;end
def H(&b)
  K.send :define_method, :x, &b
  sexp = ParseTree.translate(K, :x)
  hash_sexp = sexp[2][2]  # skip method definition stuff

  raise "block doesn't contain hash!"  if hash_sexp.first != :hash
  hash_sexp.slice! 0

  unordered_hash = yield

  oh = OrderedHash.new
  until hash_sexp.empty?
    (type, k), _ = hash_sexp.slice! 0, 2
    raise "bad key type: #{type}"  if ! [:lit, :str].include?(type)

    oh[k] = unordered_hash[k]
  end

  oh
end

This and alternative implementations are on my github.

No Comments Yet »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.