简体   繁体   中英

Accessing hashes and arrays Ruby

In my Rails application I'm sending a complex JSON string that needs to get decoded. That's not the problem I know how.

Now before I implement everything I'm trying to access some example JSON structures to see if I can get to all the variables. The problem is that the names can be variable.

{"configurations" : [ 
{ "drinks" : [
        {"menus" : [
          { "hot" : [
            {"id":15,"unit":"0.33", "price":"1", "currency":"Euro", "position": 4},
            {"id":15,"unit":"0.33", "price":"1", "currency":"Euro", "position": 6}
          ] },

          { "cold" : [
        {"id":15,"unit":"0.33", "price":"1", "currency":"Euro", "position": 4},
            {"id":15,"unit":"0.33", "price":"1", "currency":"Euro", "position": 6}
           ] },

          { "terminals" : [ {"id" : 4}, {"id": 6}, {"id": 7} ] }, 

          { "keys" : { "debit" : "on", "credit": "off" }  }

        ] }
] } ] } 

Ok, now the following fields are variable : "drinks", "hot", "cold" . All the other fields will be called the same.

Now I would like to access every variable in this JSON string after I decoded it. Before implementing it, I tryed a simple JSON:

{"configuration" : [ { "drinks" : [ { "id":15, "unit":"0.33" } ] } ] }

After decoding in rails resulting in

{ "configuration" => [{"drinks" => [{"id" => 15, "unit" => "0.33" }]}]}

Now how can I access for example id and unit without using the word "drinks". The solution should also be scalable to the example above.

Some extra information: In the large JSON I should access all the items listed there (the id's) save them to a table and return the new id and then insert it back in the JSON. (explaining why this needs to be done will take an extra page or 4 ^^ ).

i wrote this to do a deep seek in a Hash, but first something is wrong with your json i'm afraid, when you parse it you get this, you'll notice that eg terminals doesn't show all the keys. The code still has to be adapted to return an array or hash itself.

hash = JSON.parse(string) #the string you published
=>

{"configurations"=>
  [{"drinks"=>
     [{"menus"=>
        [{"hot"=>
           [{"id"=>15,
             "unit"=>"0.33",
             "price"=>"1",
             "currency"=>"Euro",
             "position"=>4},
            {"id"=>15,
             "unit"=>"0.33",
             "price"=>"1",
             "currency"=>"Euro",
             "position"=>6}]},
         {"cold"=>
           [{"id"=>15,
             "unit"=>"0.33",
             "price"=>"1",
             "currency"=>"Euro",
             "position"=>4},
            {"id"=>15,
             "unit"=>"0.33",
             "price"=>"1",
             "currency"=>"Euro",
             "position"=>6}]},
         {"terminals"=>{"id"=>7}},
         {"keys"=>{"debit"=>"on", "credit"=>"off"}}]}]}]}

Here the code

class Hash
  def dseek(search_key = "", path = "")
    self.each do|key, value|
      if value.is_a?(Hash)
        path += "#{key}."
        value.dseek(search_key, path)
      else
        if value.is_a?(Array)
          path += "#{key}."
          value.each do |val|
            val.dseek(search_key, path)
          end
        else
          puts "#{path}#{key}:#{value}" if search_key === key || search_key === ""
        end
      end
    end
  end
end


hash.dseek

gives

configurations.drinks.menus.hot.id:15
configurations.drinks.menus.hot.unit:0.33
configurations.drinks.menus.hot.price:1
configurations.drinks.menus.hot.currency:Euro
configurations.drinks.menus.hot.position:4
configurations.drinks.menus.hot.id:15
configurations.drinks.menus.hot.unit:0.33
configurations.drinks.menus.hot.price:1
configurations.drinks.menus.hot.currency:Euro
configurations.drinks.menus.hot.position:6
configurations.drinks.menus.cold.id:15
configurations.drinks.menus.cold.unit:0.33
configurations.drinks.menus.cold.price:1
configurations.drinks.menus.cold.currency:Euro
configurations.drinks.menus.cold.position:4
configurations.drinks.menus.cold.id:15
configurations.drinks.menus.cold.unit:0.33
configurations.drinks.menus.cold.price:1
configurations.drinks.menus.cold.currency:Euro
configurations.drinks.menus.cold.position:6
configurations.drinks.menus.terminals.id:7
configurations.drinks.menus.keys.debit:on
configurations.drinks.menus.keys.credit:off

and

hash.dseek("id")

gives

configurations.drinks.menus.hot.id:15
configurations.drinks.menus.hot.id:15
configurations.drinks.menus.cold.id:15
configurations.drinks.menus.cold.id:15
configurations.drinks.menus.terminals.id:7

All in all it seems a bit strange to have a hash but then not be interested in the keys, but who am I to judge, you might get that strange data from somewhere else or something.

Given the simplified example above, this is what you could do:

h["configuration"].first.values.flatten.first 
#=> {"id"=>15, "unit"=>"0.33"}

Now let's look at the more complicated example:

h["configurations"].map(&:values).flatten.first["menus"].map(&:values).flatten.map { |x| x["id"] }.compact
#=> [15, 15, 15, 15, 7]

This returns the ids, but is ugly and looses all information where the id came from. Without seeing more of your code I don't know if that is sufficient or not (it probably isn't).

In your comment, you say

every terminal has a configuration ("drinks") and can have multiple sub menu's ("hot", "cold") who all have up to 24 items (the "id" and so on)

but the JSON you posted as an example seems to reflect a different structure, that's making your task unnecessarily hard I think. If the JSON would rather be grouped hierarchically in the way that you described in your comment, your task would become really easy. It sounds like you have the possibility to change the JSON that gets generated, so I'd recommend you redesign its hierarchy instead of trying to write hard to understand/maintain code that deals with a data structure that wasn't ideal in the first place.

As an example, you say a terminal implies its configuration and menus, so I would put the terminals on the top level instead of the configurations, that is, make configuration and menus a member of the terminal instead of the other way round. This way, you can simply iterate over all of the terminals, accessing its menus, IDs and so on without having to know that this particular terminal serves "drinks" for example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM