Search code examples
rubyhashsequel

Nesting a Ruby hash


I have a method that selects all the rows from my table like this:

smtp_status_raw = my_table.select(:message, :is_valid, :hostname).map { |h| h.values }

This returns an array that's like this:

[{:message=>"blah", :is_valid=>true, :hostname=>"1"}, {:message=>"blah", :is_valid=>true, :hostname=>"2"}, {:message=>"blah", :is_valid=>true, :hostname=>"3}]

Using the above information, I want to create a hash that looks like this:

{ 
:node_status => 
    {
        {:hostname => "1", :message: "blah"},
        {:hostname => "2", :message: "blah"},
        {:hostname => "3", :message: "blah"}
    }
}

First of all, my question - is it possible to create a hash like the above? In the above example Sequel query, I have three objects which are three separate hosts, and I want to add those three hosts into a :node_status key. Is that possible? If that's not a valid hash, what is an alternative?

Second, this is what I've tried:

# Initialize the hash
smtp_status_hash = { :node_status: => nil }

I've initialized the smtp_status_hash hash with a node_status key in it, but I am not sure how to nest the query results..


Solution

  • That's not a valid hash, because you have 3 values, but no keys in the :node_status subhash. You could do something like:

    smtp_status_raw = [
      {:message=>"blah", :is_valid=>true, :hostname=>"1"},
      {:message=>"blah", :is_valid=>true, :hostname=>"2"},
      {:message=>"blah", :is_valid=>true, :hostname=>"3"}
    ]
    
    {
      node_status: smtp_status_raw.collect do |hash|
        hash.reject { |key, value| key == :is_valid }
      end
    }
    

    to get the values in :node_status as an array:

    {
      :node_status=>[
        {:message=>"blah", :hostname=>"1"},
        {:message=>"blah", :hostname=>"2"},
        {:message=>"blah", :hostname=>"3"}
      ]
    }
    

    Or you could do something like:

    {
      node_status: smtp_status_raw.collect do |hash|
        [hash[:hostname], hash[:message]]
      end.to_h
    }
    

    which sets up a sub hash with the key being the :hostname and value being :message:

    {
      :node_status=>{
        "1"=>"blah",
        "2"=>"blah",
        "3"=>"blah"
      }
    }
    

    or if you had more keys you wanted to keep:

    {
      node_status: smtp_status_raw.collect do |hash|
        [hash[:hostname], hash.reject { |key, value| key == :is_valid }]
      end.to_h
    }
    

    which is still a hash where the key is the :hostname but the value has another hash:

    {
      :node_status=>{
        "1"=>{:message=>"blah", :hostname=>"1"},
        "2"=>{:message=>"blah", :hostname=>"2"},
        "3"=>{:message=>"blah", :hostname=>"3"}
      }
    }
    

    To set the values of a key after the Hash has been created you can do something like:

    smtp_status_hash = { node_status:  nil }
    smtp_status_hash[:node_status] = "Whatever you want here"
    

    You can read more about Hash and its methods, and how you can select and reject to keep or remove keys from a hash. Hashes, though are a dictionary structure and must always have a key and a single value, though that value may be an Array or another Hash.