Turn a String Into a Hash With String.to_hash in Ruby

When used wisely, metaprogramming in Ruby can be one of the language’s most useful features. During the development of my latest project I had a need to take the results of a very complex MySQL query and turn a string into a hash. Here’s how.

We used a GROUP_CONCAT() function as part of a join to get the price of an item based on the age of a person. Because we needed to group by ID we couldn’t just loop through multiple rows so we ended up with one column that had some very important data in it that looked like this:

1
20:10.25,23:13.50,40:100

The format here is age:price,age:price... We then needed to match up and extract the data from this string. I know many will think of a million alternatives to having to parse a string into a hash but in our case this really was the only option and a very clever one if I may say so myself.

So below is the method I came up with, String#to_hash

1
2
3
4
5
6
7
8
9
10
11
12
13
class String
  def to_hash(arr_sep=',', key_sep=':')
    array = self.split(arr_sep)
    hash = {}

    array.each do |e|
      key_value = e.split(key_sep)
      hash[key_value[0]] = key_value[1]
    end

    return hash
  end
end

What we do here is first turn the string into an array by splitting it by a character you choose or , by default. Once we have an array we then set the keys and values of our new hash by iterating over each element of the new array and further splitting it by another character which separates the key from the value – : in this case. Within the loop we take the first element of the array and use it as the key in our hash, then use the second element as the value for that key. Repeat that process for every element in the original array and you have a hash.

If you use this on a string that does not have either your array separator character or your hash’s key/value separator character than you’ll get some odd results. See these examples of errors/unexpected conditions:

1
2
3
4
5
6
7
8
:002 > '0-20:100.25,28:200.1,29:100'.to_hash
 => {"0-20"=>"100.25", "28"=>"200.1", "29"=>"100"}
:002 > 'hello'.to_hash
 => {"hello"=>nil}
:003 > 'hello,world'.to_hash
 => {"hello"=>nil, "world"=>nil}
:004 > 'hello:there,world'.to_hash
 => {"hello"=>"there", "world"=>nil}

I’m using this in a Sinatra application so I have a lib folder with a patches.rb file that loads up any native class patches I make. If you’re using Rails, as most people are, I think you add this in your initializers but I’m not totally sure. I’d be surprised if Active Support didn’t already implement this anyway… (that’s a joke).

Web development

« Fixing Nokogiri installing errors on OS X (again) Making Raw HTTP Requests in Node »

Comments