rel1x
rel1x

Reputation: 2441

Convert a string with brackets to tree, Ruby

I have a string "Animals ( Reptiles Birds ( Eagles Pigeons Crows ) )" and I need to return:

a = [
  {
    "Animals" => [
      {
        "Reptiles" => nil
      },
      {
        "Birds" => [
          { "Eagles" => nil },
          { "Pigeons" => nil },
          { "Crows" => nil }
        ]
      }
    ]
  }
]

I don't understand how I can do it. Where I can find some example or what I can search in google?

Upvotes: 4

Views: 294

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110725

Here is a one way you could convert the string to an array.

Code

def arrayify(arr)
  a = split_arr(arr)
  a.map do |h|
    k = h.keys.first
    v = h.values.first
    case v
    when Array then { k => arrayify(v) }
    else { k=>v }
    end
  end
end

def split_arr(arr)
  a = []
  while arr.any?
    word = arr.shift
    if arr.empty? || arr.first != ?(
      a << { word=>nil }
    else
      arr.shift
      close_count = 0
      b = []
      loop do
        token = arr.shift
        case token
        when ?)
          break if close_count == 0
          close_count -= 1
        when ?( then close_count += 1
        end
        b << token
      end
      a << { word=>b }
    end
  end
  a
end

Example

str = "Animals ( Reptiles Birds ( Eagles Pigeons Crows ) ) Foods ( " +
      "Snacks Breakfast ( Pancakes Waffles ) )"

arrayify(str.split)

  #=> [{"Animals"=>[{"Reptiles" =>nil},
  #                 {"Birds"    =>[{"Eagles" =>nil},
  #                                {"Pigeons"=>nil},
  #                                {"Crows"  =>nil}
  #                               ]
  #                 }
  #                ]
  #    },
  #    {"Foods"  =>[{"Snacks"   =>nil},
  #                 {"Breakfast"=>[{"Pancakes"=>nil},
  #                                {"Waffles" =>nil}
  #                               ]
  #                 }
  #                ]
  #    }
  #   ]

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110725

This works with your example, but I don't know how general it is.

Code

def arrayify(str)
  eval('['+str.gsub(/(\w+)\s+\(/,'{\1=>[')
     .gsub( /(?!\{)(\w+)\s+/, '{\1=>nil},')  
     .gsub(')', ']}')
     .gsub(/\b(\w+)\b/,"\"\\1\"")+']')
end

Example

str = "Animals ( Reptiles Birds ( Eagles Pigeons Crows ) )"
arrayify(str)
  #=> [{ "Animals"=>[{ "Reptiles"=>"nil"},
  #                  { "Birds"   =>[{ "Eagles" =>"nil" },
  #                                 { "Pigeons"=>"nil" },
  #                                 { "Crows"  =>"nil" }
  #                                ]
  #                  }
  #                 ]
  #    }
  #   ]

Explanation

s1 = str.gsub(/(\w+)\s+\(/,'{\1=>[')
  #=> "{Animals=>[ Reptiles {Birds=>[ Eagles Pigeons Crows ) )"
s2 = s1.gsub( /(?!\{)(\w+)\s+/, '{\1=>nil},')  
  #=> "{Animals=>[ {Reptiles=>nil},{Birds=>[ {Eagles=>nil},{Pigeons=>nil},{Crows=>nil},) )"
s3 = s2.gsub(')', ']}')
  #=> "{Animals=>[ {Reptiles=>nil},{Birds=>[ {Eagles=>nil},{Pigeons=>nil},{Crows=>nil},]} ]}"
s4 = s3.gsub(/\b(\w+)\b/,"\"\\1\"")
  #=> "{\"Animals\"=>[ {\"Reptiles\"=>\"nil\"},{\"Birds\"=>[ {\"Eagles\"=>\"nil\"},{\"Pigeons\"=>\"nil\"},{\"Crows\"=>\"nil\"},]} ]}"
eval('['+s4+']')
  #=> <result in example>

Pardon me, but I have to run. The eval police are coming.

Upvotes: 1

Denis de Bernardy
Denis de Bernardy

Reputation: 78553

I don't understand how I can do it. Where I can find some example or what I can search in google?

Using a recursive regex is one option, especially if the parenthesis are properly balanced:

http://www.regular-expressions.info/recurse.html

If it's over your head, recursively plough through the string using a normal regex. Match something like:

[a-z]+ ?([^()]*)

... then replace the match with a place holder in the original string. Rinse, repeat.

Using a parser is an alternative option. You could write a simplistic one, or use a tool e.g.:

http://thingsaaronmade.com/blog/a-quick-intro-to-writing-a-parser-using-treetop.html

Upvotes: 1

Related Questions