1

I'm using Ruby 2.2 and have a string that looks like this:

myvar = '{"myval1"=>"value1","mayval2"=>"value2"}'

How can I get this into a key-value pair and/or hash of some sort? When I do myvar['myval1'] I get back 'myval1', which isn't quite what I'm after. The answer's probably staring right at me but nothing's worked so far.

jkj2000
  • 1,563
  • 4
  • 19
  • 26
  • I lost my close vote, but this is a duplicate. [How do I convert a String object into a Hash object?](http://stackoverflow.com/questions/1667630/how-do-i-convert-a-string-object-into-a-hash-object) – Roman Kiselenko May 03 '16 at 15:56
  • Where does that string come from? Is it possible to get the input in a different format? – spickermann May 03 '16 at 16:08
  • @spickermann we're getting it back as part of an external api call, whose output we can't control. – jkj2000 May 03 '16 at 16:09

2 Answers2

3

As I've seen times and times again - simply mentioning eval makes people instantly upset, even if it was a proper use case (which this is not).

So I'm going to go with another hate magnet - parsing nested structures with regexes.


Iteration (1) - a naive approach:

JSON.parse(myvar.gsub(/=>/, ':'))

Problem - will mess up your data if the string key/values contain =>.


Iteration (2) - even number of "s remaining mean you are not inside a string:

JSON.parse(myvar.gsub(/=>(?=(?:[^"]*"){2}*[^"]*$)/, ':'))

Problem - there might be a " inside a string, that is escaped with a slash.


Iteration (3) - like iteration (2), but count only " that are preceded by unescaped slashes. An unescaped slash would be a sequence of odd number of slashes:

eq_gt_finder = /(?<non_quote>
                  (?:
                     [^"\\]|
                     \\{2}*\\.
                  )*
                ){0}
                =>(?=
                     (?:
                        \g<non_quote>
                        "
                        \g<non_quote>
                     ){2}*
                     $
                  )/x
JSON.parse(myvar.gsub(eq_gt_finder, ':'))

See it in action


Q: Are you an infallible divine creature that is absolutely certain this will work 100% of the time?

A: Nope.

Q: Isn't this slow and unreadable as shit?

You're god damn right.

Q: Ok?

A: Yep.

ndnenkov
  • 35,425
  • 9
  • 72
  • 104
2

You can change that string to valid JSON easily and use JSON.parse then:

require 'JSON'
myvar = '{"myval1"=>"value1","mayval2"=>"value2"}'

hash = JSON.parse(myvar.gsub(/=>/, ': '))
#=> { "myval1"  => "value1", "mayval2" => "value2" }

hash['myval1']
#=> "value1"
spickermann
  • 100,941
  • 9
  • 101
  • 131
  • you might want to read [my answer](http://stackoverflow.com/a/37009796/2423164), which expands on the subject. I hope you find it stimulating (or at least entertaining). – ndnenkov May 03 '16 at 16:47