fetch the Hash from a string

require 'json'

str = "1=2,3=(4=5,6=7)" new_str = str.gsub(/=|\(|\)/) do |m|   if m == "="     m= "=>"   elsif m == "("     m = "{"   else     m = "}"   end end

new_str = new_str.prepend("{") << "}" # => "{1=>2,3=>{4=>5,6=>7}}"

JSON.parse(new_str) # ~> /home/kirti/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/json/common.rb:155:in `parse': 757: unexpected token at '{1=>2,3=>{4=>5,6=>7}}' (JSON::ParserError) # ~> from /home/kirti/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/json/common.rb:155:in `parse' # ~> from -:15:in `<main>'

My question is how should I get the hash {1=>2,3=>{4=>5,6=>7}} from that string?

Why don't you generate valid JSON in the first place?

`{1=>2,3=>{4=>5,6=>7}}.to_json` would show you what the string should look like (and why your example is so broken).

Hassan Schroeder wrote in post #1119612:

Hassan Schroeder wrote in post #1119612:

If you must use that format --

2.0.0-p247 :017 > str => "1=2,3=(4=5,6=7)" 2.0.0-p247 :018 > eval str.tr('()','{}').gsub(/=/,'=>').prepend('{').concat('}') => {1=>2, 3=>{4=>5, 6=>7}} 2.0.0-p247 :019 >

require 'json'

str = "1=2,3=(4=5,6=7)" new_str = str.gsub(/=|\(|\)/) do |m| if m == "="    m= "=>" elsif m == "("    m = "{" else    m = "}" end end

new_str = new_str.prepend("{") << "}" # => "{1=>2,3=>{4=>5,6=>7}}"

JSON.parse(new_str) # ~> /home/kirti/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/json/common.rb:155:in `parse': 757: unexpected token at '{1=>2,3=>{4=>5,6=>7}}' (JSON::ParserError) # ~> from /home/kirti/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/json/common.rb:155:in `parse' # ~> from -:15:in `<main>'

My question is how should I get the hash {1=>2,3=>{4=>5,6=>7}} from that string?

-

There is no JSON here at all.

Given this:

new_str = new_str.prepend("{") << "}" # => "{1=>2,3=>{4=>5,6=>7}}"

You have a string which represents a what a Hash looks like. Why not run it through eval and see what you get?

my_hash = eval(new_str)

tamouse m. wrote in post #1119628:

If all your keys and values are just digits (i.e., String#to_i likes them), then you can easily avoid eval and yaml. Just make your own parser. Here are some "learning tests" to get you started. If you avoid scrolling down too far to see my "answer", then just make the tests pass and you're done!

Of course, you can also expand the tests to cover even more exotic hash keys and values, but if you go too far you've just reinvented YAML or JSON with different syntax.

-Rob

require 'minitest/autorun'

class TestSomeCrappyMarkupLanguage < Minitest::Test   def setup     @str = "1=2,3=(4=5,6=7)"   end

  def test_nil     refute SomeCrappyMarkupLanguage.parse(nil)   end

  def test_empty_string     assert_equal({}, SomeCrappyMarkupLanguage.parse(""))   end

  def test_simple_hash     assert_equal({1=>2}, SomeCrappyMarkupLanguage.parse("1=2"))   end

  def test_two_elements     assert_equal({4=>5,6=>7}, SomeCrappyMarkupLanguage.parse("4=5,6=7"))   end

  def test_nested     expected = { 1 => 2, 3 => { 4 => 5, 6 => 7 } }     assert_equal expected, SomeCrappyMarkupLanguage.parse(@str)   end

end

# spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below... # spoilers below...

class SomeCrappyMarkupLanguage   def self.parse(str)     return nil unless str     result = {}     str.scan(/(\d+)=((?:\([^\)]*\))|\d+),?/).each do |key,value|       key = key.to_i       value = value =~ /\A\d+\z/ ? value.to_i : parse(value)       result[key] = value     end     result   end end

Rob Biedenharn wrote in post #1119642:

@Rob - thanks for such an detailed answer.. :slight_smile: I need to give some more time on this...