separating key and multiple values for print with .each - ruby

I'm probably trying to be hard headed about this. I'm trying to format hash key and and array of values for output to user. Ruby-doc give me the code for it for one value. http://www.ruby-doc.org/core/classes/Hash.html#M002861
h = { "a" => 100, "b" => 200 }
h.each {|key, value| puts "#{key} is #{value}" }
I'm trying to get
h = { "a" => [100,'green'], "b" => [200,'red'] }
h.each {|key, m,n| puts "#{key} is #{m} and #{n}"}
produces:
a is 100 and green
b is 200 and red
I've had some luck with
h.each{|key,m,n| puts "#{key} is #{[m,'n']} "}
it produces:
a is 100green
b is 200red
I need some space between my array of elements, how do I go about doing that?

h.each {|key, (m, n)| puts "#{key} is #{m} and #{n}"}

h.each { |key, value| puts "#{key} is #{value.first} and #{value.last}" }

I'm a fan of each_pair for hashes:
h.each_pair {|key, val| puts "#{key} is #{val[0]} and #{val[1]}" }
Or
h.each_pair {|key, val| puts "#{key} is #{val.join(' and ')}"}

h.each {|k,v| puts "#{k} is #{v[0]} and #{v[1]}"}

Related

Hash keys and values

I would like to know if you have any more practical way of working with keys, value in ruby.
I had to improvise to be able to access and print the keys Rafael and Roberto and at the same time print their keys and values
My code:
arr = Hash.new
arr["Rafael"] = []
arr["Roberto"] = []
listaProdutos = [
"banana",
"uva",
"biscoito"
]
listaProdutos.each{ |i|
arr["Rafael"] << {"Produto": i, "Quantidade": rand(1..9)}
arr["Roberto"] << {"Produto": i, "Quantidade": rand(1..9)}
}
arr.each{ |k,lista|
(0..arr.count).each do |i|
puts "#{k} vai comprar #{arr[k][i][:Quantidade]} unidades de #{arr[k][i][:Produto]}"
end
}
This:
arr.each{ |k,lista|
(0..arr.count).each do |i|
puts "#{k} vai comprar #{arr[k][i][:Quantidade]} unidades de #{arr[k][i][:Produto]}"
end
}
Can we rewritten as:
arr.each do |k, lista|
lista.each do |obj|
puts "#{k} vai comprar #{obj[:Quantidade]} unidades de #{obj[:Produto]}"
end
end

Ruby: How do I remove nil/empty values from a nested hash and why aren't my attempts doing so?

I'm trying to write a method that removes all keys in a nested hash that point to nil recursively.
For example:
{:a=>nil, :b=>"b", :c=>nil, :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
becomes:
{:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
I'm not having much luck though.
My most recent attempt looks like:
def deep_compact(hash)
hash.reject do |key, value|
deep_compact(value) if value.class == Hash
next true if value.nil? || value.empty?
end
end
Here I want to iterate over each key value pair in the hash. If the value is a hash, I want to do the same for that hash. I want to reject the pair if the value is nil or empty. Otherwise, I want to keep it.
The result isn't what I want:
#=> {:b=>"b", :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
I have also tried:
def deep_compact(hash)
hash.compact.transform_values do |value|
deep_compact(value) if value.class == Hash
value
end
end
Again, I get the same result:
#=> {:b=>"b", :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
This leaves me to believe that either I've missed something or my understanding of recursion is wrong.
Are any of my attempts close? What do I need to do to ensure I get the result I want: {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}?
The trick would be to recursively compact nested hashes and then to eliminate empty values.
compact = ->(hash) {
hash.is_a?(Hash) ?
hash.map { |k, v| [k, compact.(v)] }.
to_h.
delete_if { |_, v| v.nil? || v.respond_to?(:empty?) && v.empty? } :
hash
}
compact.(input)
#⇒ {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
I discovered that by placing my recursive function call at the end of the block got me most of the way there. (Is this 'tail-end' recursion?)
I also call reject on the hash returned by transform_values to removed any empty pairs.
This achieves what I wanted:
def deep_compact(hash)
hash.compact.transform_values do |value|
next value unless value.class == Hash
deep_compact(value)
end.reject { |_k, v| v.empty? }
end
> h
=> {:a=>nil, :b=>"b", :c=>nil, :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
> deep_compact h
=> {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
Other option using Hash#reject!, it changes the original Hash:
def deep_compact(h)
h.each { |_, v| deep_compact(v) if v.is_a? Hash }.reject! { |_, v| v.nil? || v.empty? }
end
deep_compact(h)
#=> {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}

compare two hashes in ruby

I need to compare 2 hashes in ruby where one of the hash contains the numeric values in quoted string, which makes that a string. Consider the following 2 hashes:
hash1 = {"A"=>"0", "B"=>"1", "SVHTID"=>"VH", "D"=>"0", "E"=>"19930730", "F"=>"TEST - DEPOSIT", "G"=>"2.25000000"}
hash2 = {"a"=>"0", "b"=>1, "c"=>"VH", "d"=>0,"e"=>19930730, "f"=>"TEST - DEPOSIT", "g"=>2.25}
Now the code i have written so far is as follows:
hash2 = Hash[hash2.map {|key, value| [key.upcase, value] }]
hash1.each{|k,v| hash1[k] = hash1[k].to_i if hash1[k].match(/-?\d+(?:\.\d+)?/)}
hash1.keys.select { |key| hash1[key] != hash2[key] }.each { |key|
puts "expected #{key} => #{hash1[key]}, but found #{key} => #{hash2[key]}"
}
what is does is that it also converts the float value to integer and the original value is lost.
What i want is that when the above 2 hashes are compared the output should contain only G as mismatched type and following should be printed:
Expected: G=>2.25000000 but found G=>2.25
# normalization
h1, h2 = [hash1, hash2].map do |h|
h.map do |k, v|
[k.to_s.downcase, v.to_s.to_i.to_s == v.to_s ? v.to_i : v]
end.to_h
end
Now we are ready to compare:
h1.reject { |k, v| h2[k].nil? || h2[k] == v }
#⇒ { "g" => "2.25000000" }
This might be printed, formatted etc as you want.

Call a Hash value with a variable - Ruby

How can I call a hash value with a variable?
I have a hash like this:data = {"5/3/2013 13:31:13"=>{:open=>65, :closed=>835}}
datasequences.each do |seq_title|
sequence = Hash.new(0)
sequence[:title] = seq_title
sequence_data = Array.new(0)
data.each do |key, value|
puts value[#{seq_title.to_sym}]
# More code...
end
end
The per the Hash (data), the value of seq_title will be open and then closed.
For example if I change the code to read
datasequences.each do |seq_title|
sequence = Hash.new(0)
sequence[:title] = seq_title
sequence_data = Array.new(0)
data.each do |key, value|
puts value[:open]
# More code...
end
end
In the above code Ruby outputs 65, but I want value[var] (not hardcoded) so it can output 65 and through the next iteration 835.
You just need to specify the key with a variable:
% irb
irb> data = { "5/3/2013 13:31:13"=>{:open=>65, :closed=>835}, "5/4/2013 14:41:14"=>{:open=>56, :closed=>538} }
# => {"5/3/2013 13:31:13"=>{:open=>65, :closed=>835}, "5/4/2013 14:41:14"=>{:open=>56, :closed=>538}}
irb> datasequences = [ :open, :closed ]
# => [:open, :closed]
irb> datasequences.each do |seq_title|
irb| puts "seq_title is #{seq_title.inspect}"
irb| data.each do |key, value|
irb| puts "\t#{value[seq_title]} at #{key}"
irb| end
irb| end
seq_title is :open
65 at 5/3/2013 13:31:13
56 at 5/4/2013 14:41:14
seq_title is :closed
835 at 5/3/2013 13:31:13
538 at 5/4/2013 14:41:14
# => [:open, :closed]
Since the value is itself a hash, you print the keys and values like this:
data.each do |key, value|
value.each do |k,v|
puts "#{k}: #{v}"
end
# More code...
end
You need to use Hash#values_at, like below:
data = {"5/1/2013 10:42:40" => {"open" => 10,"closed" => 345},"5/2/2013 10:42:40" => {"open" => -1,"closed" => 700}}
datasequences = [ :open, :closed ]
data.each do |k,v|
p v.values_at(*datasequences.map(&:to_s))
end
output:
[10, 345]
[-1, 700]

display values of couchdb document

I have a document stored in couchdb which is like this -
{
"_id": "0f8baf09c680abdc434607dc77000bad",
"_rev": "2-c989a4c672d25b678aadfa4c37212404",
"XI": "gl11subjects",
"XII": "gl12subjects"
}
when I retrieve the document using the code
#value = CouchRest.get("url/db/docid")
#value.each do |key, value|
puts "Key: "+key+"value: "+value+
The above statement prints all the values in the json file, including _id and _rev.
How should I iterate through the #value to only get the key/value for XI and XII.
Here's one way:
#value.each do |key, value|
puts "Key: #{key} Value: #{value}" if key.include?("XI")
end
Inversely, you could do the following:
#value.each do |key, value|
puts "Key: #{key} Value: #{value}" unless key.include?("_")
end

Resources