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

theGreenCabbage
  • 5,197
  • 19
  • 79
  • 169

2 Answers2

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.

Simple Lime
  • 10,790
  • 2
  • 17
  • 32
  • 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? – theGreenCabbage Aug 08 '17 at 18:01
  • 1
    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) – Simple Lime Aug 08 '17 at 18:28
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)}