tags:

views:

241

answers:

4

I have a map generated by others with data structure like this:

x = {"city": "London", "country": "England", "region": "Europe"}

I want to manipulate the data in Ruby. So in order to be able to let Ruby understand it's a map, I need to replace all ":" to "=>". Is there a quick way to accomplish this in one line?

+2  A: 
'{"city": "London", "country": "England", "region": "Europe"}'.gsub!(/: /, ' => ')

gsub! does an in-place global substitution on the string.

mipadi
+5  A: 
my_text = 'x = {"city": "London", "country": "England", "region": "Europe"}'

# Replace : with =>
ruby_code = t.gsub(':', '=>')

# Evaluate the string as ruby code
eval ruby_code

# Now you can access the hash

x['city'] # => "London"
collimarco
Only do this if you're sure that there'll never be colons in either the keys or the values! If there's any possibility of that, use the JSON method Subba Rao suggested.
glenn mcdonald
Also, be sure that your strings don't contain anything that eval could interpret as ruby interpolation, like '{"don\'t do this": "#{`rm -rf ~`}"}' - that would be bad
rampion
This is very unsafe and fragile. Using "eval" is almost always wrong. Subba Rao's answer is much better.
Jason Creighton
While might deceptively work sometimes, this goes against everything I was ever taught. This is dangerous and could explode in your face very easily.
Justin L.
+13  A: 

you need to install this gem json

sudo gem install json

    require 'json'
    JSON.parse('{"city": "London", "country": "England", "region": "Europe"}')
Subba Rao
I really like this answer. Even though it's likely slower than collimarco's (which I also like), it seems to get at the underlying meaning of what's going on here much better. It's very unlikely that the JSON format for maps/hashes will change, so there's no real technical benefit. It just _feels_ nice.
James A. Rosen
It also avoids executing the foreign input as code.
If anything gets screwy in the input, this way will throw a JSON parse error. The gsub way will, well, it could do pretty much anything! Really great reason to prefer this way.
glenn mcdonald
A: 

Another alternative, if you wanted to minimize your vulnerability to #eval (which is a rational thing to do) is to use String#scan:

quoted = /"(?:\\.|[^\\"])*"/
pairs = '{"city": "London", "country": "England", "region": "Europe"}'.scan(/(#{quoted})\s*:\s*(#{quoted})/)
hash = Hash[ *pairs.flatten.map { |q| eval q } ]

We're still using #eval, but we know that we're only #eval'ing something that looks like a quoted string, so we're safe-ish. Since ruby strings can contain arbitrary code (through the miracle of interpolation), we're still vulnerable, though:

# arbitrary code evaluation
p eval('"one two #{puts %{Give me a three!}; gets.chomp} four"')

The safest alternative is to bypass #eval entirely, and use a library for unquoting strings that doesn't allow for interpolation. The JSON library (as previously mentioned) is a great example of that. It may seem slower (since it's not as optimized as #eval is), but it's a whole heck of a lot safer.

rampion