olivier olivier - 1 month ago 8
Ruby Question

Traverse JSON tree and in Ruby and format

my request returns any type of JSON. I would like to give a key to each attribute recursively in Ruby.

{
"name": "My name",
"age": 17,
"users": [
{
"name": "John Doe",
"colours": [
"blue",
"red"
],
"animals": [
{
"name": "Panthera"
}
]
}
]
}


It should traverse the tree and return:

[ 'name', 'age', 'users[]' 'users[]name', 'users[]colours', 'users[]animals', users[]animals[]name' ]


I can get top-level keys by calling
Hash.keys
but not sure how to traverse the tree and return custom key. Thanks for your help.

j = {"name":"My name","age":17,"users":[{"name":"John Doe","colours":["blue","red"],"animals":[{"name":"Panthera"}]}]}
a = []
j.keys.each do |key|
if key.class == Array
a << "#{key}[]"
else
a << key
end
end
a

Answer

This seems to achieve what you want. You were on the right track thinking recursively. I added comments to explain what's happening.

require 'json'

# Convert JSON to a ruby hash
j = JSON.parse('{"name":"My name","age":17,"users":[{"name":"John Doe","colours":["blue","red"],"animals":[{"name":"Panthera"}]}]}')
a = []

def make_array_from_hash_keys(array, hash, prefix = "")
  # Iterate through the hash's key/value pairs
  hash.each do |key, value|
    # If the value is an array, we set a new prefix and add that to the main
    # array. Then we iterate through this inner array to see if there are more
    # hashes. If there are, call the parent method to generate more elements
    # for the array, using the new prefix.
    if value.class == Array
      new_prefix = "#{prefix + key}[]"
      array << new_prefix

      hash[key].each do |array_value|
        if array_value.class == Hash
          # |= makes sure that the final array elements are unique
          array |= make_array_from_hash_keys(array, array_value, new_prefix)
        end
      end

    # If the value is not an array, simply add it to the main array with the
    # necessary prefix
    else
      array << prefix + key
    end
  end

  array
end

new_array = make_array_from_hash_keys(a, j)
puts new_array.inspect
# Gives us:
# ["name", "age", "users[]", "users[]name", "users[]colours[]", "users[]animals[]", "users[]animals[]name"]