Hash with array of hashes inside hash - arrays

I have a hash like this:
document = {
"results" => {[
{"ip" => 10, "host" => 12},
{"ip" => 13, "host" => 17}
]}
}
It's a one item hash with an array of hashes inside the hash. I specified a value of ip = 10.
If the ip is more than 10, I want to print both keys with values. This hash is very complicated and I don't know how to access these values. Can you help me?
Edit:
What if I had hash like this
document = { "results" => [{"ip" => 10, "host" => 12, "temp" => yes},{"ip" => 13, "host" => 17, "temp" => yes}] } and wanted print only ip and host after matching ip with 10?

document["results"].each do |result|
if result["ip"] > 10
puts result # will print ip and host
end
end

I would use select:
document = { "results" => [{"ip" => 10, "host" => 12},{"ip" => 13, "host" => 17}] }
puts document['results'].select { |hash| hash['ip'] > 10 }
#=> {"ip"=>13, "host"=>17}
Explanation:
document['results']
returns the array of hashes:
[{"ip" => 10, "host" => 12},{"ip" => 13, "host" => 17}]
In the next step select is called on that returned array:
document['results'].select { |hash| hash['ip'] > 10 }
This returns all sub-hashes with an value > 10 assigned to the key 'ip'.
puts just prints the result to STDOUT

I have another problem today. Here is my code:
require 'rubygems'
require 'json'
document = JSON.load File.new("hosts.txt")
file = JSON.load File.new("admins.txt")
first_table = document["results"]
second_table = file["admins"]
new_one = first_table | second_table
First hash looks like this:
document = { "results" => [{"ip" => 10, "host" => 12},{"ip" => 13, "host" => 17}] }
The second hash is
file = { "admins" => [{"host" => 12, "name" => 12},{"host" => 17, "name" => 17}] }
I want merge these two hashes matching them by host by the same value to get
{ "new_one" => [{"ip" => 10, "host" => 12, "name" => 12}, {"ip" => 13, "host" => 17}, "name" => 17]
When I try new_one = first_table | second_tableit says test.rb:24:in <main>': undefined method|' for #Hash:0x00000002ca8be8 (NoMethodError) and when I try new_one = first_table.merge(second_table)its says test.rb:26:in <main>': undefined methodmerge' for #Array:0x00000002ce88b0(NoMethodError). So what is wrong with these hashes? One time they are hashes and the second time they are arrays? How to mach these hashes? The keys and values of host the same in both hashes.

Related

How to return all even numbers inside an array inside of a hash?

I've been given the following data structure:
users = {
"Jonathan" => {
:twitter => "tronathan",
:favorite_numbers => [12, 42, 75],
},
"Erik" => {
:twitter => "sferik",
:favorite_numbers => [8, 12, 24],
},
"Anil" => {
:twitter => "bridgpal",
:favorite_numbers => [12, 14, 85],
},
}
I need to return all of Anils favourite numbers that are even.
This is what I have so far:
users["Anil"][:favorite_numbers].each do |evennum|
if evennum.even?
puts evennum
end
end
You could do something like this
anil_favorite_even_numbers = users['Anil'][:favorite_numbers].select(&:even?)
This takes for granted that a user Anil exists and the favourite_numbers inside it too and that's an array. Otherwise we need a little bit of extra work.

Efficient way to update values to array of hashes in ruby?

I have an array of hashes like below:
items = [ {"id" => 1, "cost" => '2.00'},
{"id" => 2, "cost" => '6.00'},
{"id" => 1, "cost" => '2.00'},
{"id" => 1, "cost" => '2.00'},
{"id" => 1, "cost" => '2.00'} ]
I would like to update the cost to '8.00' where the id = 1. I have tried with the each method like below which does work but I would like to know if there is another more efficient way of updating the values?
items.each { |h| h["cost"] = "8.00" if h["id"] == 1 }
You could just use the same object:
item_1 = {'id' => 1, 'cost' => '2.00'}
item_2 = {'id' => 2, 'cost' => '6.00'}
items = [item_1, item_2, item_1, item_1, item_1]
#=> [{"id"=>1, "cost"=>"2.00"}, {"id"=>2, "cost"=>"6.00"},
# {"id"=>1, "cost"=>"2.00"}, {"id"=>1, "cost"=>"2.00"},
# {"id"=>1, "cost"=>"2.00"}]
This makes updates trivial:
item_1['cost'] = '8.00'
items
#=> [{"id"=>1, "cost"=>"8.00"}, {"id"=>2, "cost"=>"6.00"},
# {"id"=>1, "cost"=>"8.00"}, {"id"=>1, "cost"=>"8.00"},
# {"id"=>1, "cost"=>"8.00"}]
You might consider changing your data structure from:
items = [{"id" => 1, "cost" => '2.00'}, {"id" => 2, "cost" => '6.00'},
{"id" => 1, "cost" => '2.00'}, {"id" => 1, "cost" => '2.00'},
{"id" => 1, "cost" => '2.00'}]
To a hash like this:
items = { 1 => '2.00', 2 => '6.00' }
To updating the record with id = 1 to 8.00 call:
items[1] = '8.00'
Or if you need to know the number of items, you might want to conside a structure like this:
items = { 1 => ['2.00', 4], 2 => ['6.00', 1] }
Than update like this:
items[1][0] = '8.00'
You can achieve this by using each on array
items.each{|v| v["cost"] = "8.00" if v["id"] == 1 }
Cheers!

Ruby pick up a value in hash of array to reformat into a hash

Is there a way I can pick a value in hash of array, and reformat it to be only hash?
Is there any method I can do with it?
Example
[
{
"qset_id" => 1,
"name" => "New1"
},
{
"qset_id" => 2,
"name" => "New2"
}
]
Result
{
1 => {
"name" => "New1"
},
2 => {
"name" => "New2"
}
}
You can basically do arbitary manipulation using reduce function on array or hashes, for example this will get your result
array.reduce({}) do |result, item|
result[item["qset_id"]] = { "name" => item["name"] }
result
end
You can do the same thing with each.with_object do:
array.each.with_object({}) do |item, result|
result[item["qset_id"]] = { "name" => item["name"] }
end
it's basically the same thing but you don't have to make each iteration return the result (called a 'memo object').
You could iterate over the first hash and map it into a second hash:
h1.map{|h| {h['qset_id'] => {'name' => h['name']}} }
# => [{1=>{"name"=>"New1"}}, {2=>{"name"=>"New2"}}]
... but that would return an array. You could pull the elements into a second hash like this:
h2 = {}
h1.each do |h|
h2[h['qset_id']] = {'name' => h['name']}
end
>> h2
=> {1=>{"name"=>"New1"}, 2=>{"name"=>"New2"}}

Iterate into an array of hash

I'm trying to loop into an array of hash :
response = [
{
"element" => A,
"group" => {"created" => 13, "code" => "Paris.rb", :"rsvp_limit" => 40},
"name" => "CODELAB",
"venue" => {"id" => 17485302, "place" => "la cordée", "visibility" => "public"}
},
{
"element" => B,
"group" => {"created" => 13, "code" => "Paris.rb", :"rsvp_limit" => 40},
"name" => "PARISRB",
"venue" => {"id" => 17485302, "place" => "la cordée", "visibility" => "public"}
}
]
When I run
response[0]["name"], it returns "CODELAB"
response[1]["name"] returns "PARISRB"
How can I make a loop to have the name of each element of this array of hash ?
I tried :
response.each_with_index do |resp, index|
puts array[index]["name"]
end
This is the error I get in my console :
NoMethodError: undefined method `[]' for nil:NilClass
from (pry):58:in `block in __pry__'
The array here seems to be a typo. You meant:
response.each_with_index do |resp, index|
puts resp["name"]
end
The index is not needed because the resp variable is correctly initialized to contain the hash at each iteration.
So it can be simplified to:
response.each do |resp|
puts resp["name"]
end
A little bit shorter:
puts response.map{|hash| hash['name']}
# CODELAB
# PARISRB

Return Array of Objects/Hashes sorted by attributes

I have an array of objects, in this example represented as hashes:
[
{ "mid" => 123, "updated" => "2015-05-05 05:05:05"}
{ "mid" => 456, "updated" => "2015-05-05 05:06:05"}
{ "mid" => 789, "updated" => "2015-05-05 05:05:05"}
{ "mid" => 123, "updated" => "2015-05-05 05:05:07"}
{ "mid" => 456, "updated" => "2015-05-05 05:05:05"}
]
And I need to get back only elements with the unique mid, in a process of selecting unique objects, timestamp needs to be taken to consideration, where higher datetime is the one to return. So my examples outcome would be:
[
{ "mid" => 456, "updated" => "2015-05-05 05:06:05"}
{ "mid" => 789, "updated" => "2015-05-05 05:05:05"}
{ "mid" => 123, "updated" => "2015-05-05 05:05:07"}
]
I tried couple of different approaches, but I have a problem with my logic, I always get 20 objects back instead of 3. Here is my code:
res = []
queue.length.times do
a = queue.pop
queue.each do |job|
if a.mid == job.mid
if DateTime.parse(a.updated) >= DateTime.parse(a.updated)
res << a
end
else
res << a
end
end
queue << a
end
Any idea?
Assuming you have your input in arr:
arr.group_by do |h|
h['mid'] # group same mids
end.values.map do |all|
all.max_by do |h|
Date.parse h['updated'] # pick latest by date
end
end
#⇒ [
# {"mid"=>123, "updated"=>"2015-05-05 05:05:07"},
# {"mid"=>456, "updated"=>"2015-05-05 05:06:05"},
# {"mid"=>789, "updated"=>"2015-05-05 05:05:05"}
# ]
Please avoid writing phpish code using ruby syntax.
UPD Credits to Cary Swoveland, sort_by+last reduced to max_by.
You can use something like:
queue.sort_by { |r| DateTime.parse(r[:updated]) }.reverse.uniq { |r| r[:mid] }
# => [{:mid=>456, :updated=>"2015-05-05 05:06:05"}, {:mid=>123, :updated=>"2015-05-05 05:05:07"}, {:mid=>789, :updated=>"2015-05-05 05:05:05"}]
where queue is your input array.

Resources