Nesting a Ruby hash
Asked Answered
A

2

5

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..

Abba answered 7/8, 2017 at 20:53 Comment(0)
S
6

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.

Source answered 7/8, 2017 at 21:21 Comment(2)
Hey Simple. Can you explain the difference between my "desired" hash in my question, vs the first hash in your example? It seems you turned it into an array, right?Abba
Yeah it's just an array nested in :node_status instead of a hash. So the only reason your desired hash wasn't a valid hash was because you had :node_status => {...} instead of :node_status => [...] which would be telling Ruby that you wanted a key/value pair for each of the nodes, instead of just a list of them, and only giving values (or, only giving keys with no values)Source
E
0

Try this

smtp_status_hash = {:node_status=>[]}; my_table.select(:message, :is_valid, :hostname).map{ |h| h.values }.each{|i| smtp_status_hash[:node_status] << (i)}

Ezzell answered 7/8, 2017 at 21:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.