Recursively Symbolize Keys

Given a YAML stream that looks like this:

---
drinks:
  martini:
    garnish: olive
  gibson:
    garnish: onion

The Ruby-fied version looks like this:

{"drinks"=>{"gibson"=>{"garnish"=>"onion"}, "martini"=>{"garnish"=>"olive"}}}

But we don’t like string keys, we like symbol keys. A number of libraries exist to extend Hash with interchangeable string- or key-based indexing. But adding a library dependency seems like overkill for symbolizing some keys. Here’s a quick recursive string-to-symbol key converter:

def symbolize_keys(hash)
  hash.inject({}){|result, (key, value)|
    new_key = case key
              when String then key.to_sym
              else key
              end
    new_value = case value
                when Hash then symbolize_keys(value)
                else value
                end
    result[new_key] = new_value
    result
  }
end

Note the block parameters to Hash#inject.

  hash.inject({}){|result, (key, value)|

The block arguments would normally be a hash (the accumulator) and a two-element array of [key, value]. Putting parentheses around the second two parameters causes the second argument to be “splatted” into its component parts and assigned to key and value separately.

Let’s take a look at the output.

puts "Symbolized hash:"
puts symbolize_keys(YAML.load(yaml)).inspect
Symbolized hash:
{:drinks=>{:martini=>{:garnish=>"olive"}, :gibson=>{:garnish=>"onion"}}}

9 comments

  1. BTW you can also use symbols in YAML, just do something like that:


    :key: value

    About your example, I personally prefer to create SymbolizedKeysMixin, include it to the Hash class and use key.respond_to?(:symbolize_keys) rather than testing class of each key. So the only thing for supporting another classes will be e. g. Mash.send(:include, SymbolizedKeysMixin).

    1. Yeah, I know about the symbol syntax in YAML. But a lot of YAML formats don't use it, presumably because the string syntax is more pleasant to read, and perhaps because insisting on adding the file's symbols to the system string table is arguably presumptuous. Or maybe just because it's easy to forget to type that leading colon.

      A running theme in my work is solutions that don't require extending core classes, but there are certainly advantages to the module inclusion approach.

Leave a Reply

Your email address will not be published. Required fields are marked *