I am a beginner and am looking for a method to iterate through a hash containing hashed values. E.g. I only want to print a list of all values of inner hash-elements of the key named "label". My array is looking like this:
ary = Hash.new
ary[:item_1] = Hash[ :label => "label_1" ]
ary[:item_2] = Hash[ :label => "label_2" ]
ary[:item_3] = Hash[ :label => "label_3" ]
Now I want to iterate through all elements of the outer hash and try this:
ary.keys.each { |item| puts ary[:item] }
or this:
ary.keys.each { |item[:label]| puts ary[:item] }
Unfortunately both do not work. But if I try this - quite crazy feeling - detour, I get the result, which is I want to:
ary.keys.each { |item|
evalstr = "ary[:" + item.to_s + "][:label]"
puts eval(evalstr)
}
This yields the result:
label_1
label_2
label_3
I am absolutely sure that there must exist be a better method, but I have no idea how to find this method.
Thanks very much for your hints!
You can iterate through all values of an hash using each_value; or, you can iterate trough all key/value pairs of an hash using each, which will yield two variables, key and value.
If you want to print a single value for each hash value of the outer hash, you should go for something like this:
ary.each_value { |x| puts x[:label] }
Here's a more complex example that shows how each works:
ary.each { |key, value| puts "the value for :label for #{key} is #{value[:label]}" }
ary = Hash.new
ary[:item_1] = Hash[ :label => "label_1" ]
ary[:item_2] = Hash[ :label => "label_2" ]
ary[:item_3] = Hash[ :label => "label_3" ]
ary.flat_map{|_,v| v.values}
#=>["label_1", "label_2", "label_3"]
See Hash methods
> ary = Hash.new
> ary[:item_1] = Hash[ :label => "label_1" ]
> ary[:item_2] = Hash[ :label => "label_2" ]
> ary[:item_3] = Hash[ :label => "label_3" ]
> ary.values.each {|v| puts v[:label]}
label_1
label_2
label_3
Related
I have:
people=["Bob","Fred","Sam"]
holidays = Hash.new
people.each do |person|
a=Array.new
holidays[person]=a
end
gifts = Hash.new
people.each do |person|
a=Array.new
gifts[person]=a
end
Feels clunky. I can't seem to figure a more streamline way with an initialization block or somesuch thing. Is there an idiomatic approach here?
Ideally, I'd like to keep an array like:
lists["holidays","gifts",...]
... and itterate through it to initialize each element in the lists array.
people = %w|Bob Fred Sam|
data = %w|holidays gifts|
result = data.zip(data.map { people.zip(people.map { [] }).to_h }).to_h
result['holidays']['Bob'] << Date.today
#⇒ {
# "holidays" => {
# "Bob" => [
# [0] #<Date: 2016-11-04 ((2457697j,0s,0n),+0s,2299161j)>
# ],
# "Fred" => [],
# "Sam" => []
# },
# "gifts" => {
# "Bob" => [],
# "Fred" => [],
# "Sam" => []
# }
# }
More sophisticated example would be:
result = data.map do |d|
[d, Hash.new { |h, k| h[k] = [] if people.include?(k) }]
end.to_h
The latter produces the “lazy initialized nested hashes.” It uses the Hash#new with a block constructor for nested hashes.
Play with it to see how it works.
A common way of doing that would be to use Enumerable#each_with_objrect.
holidays = people.each_with_object({}) { |p,h| h[p] = [] }
#=> {"Bob"=>[], "Fred"=>[], "Sam"=>[]}
gifts is the same.
If you only want a number of such hashes then, the following should suffice:
count_of_hashes = 4 // lists.count; 4 is chosen randomly by throwing a fair die
people = ["Bob", "Fred", "Sam"]
lists = count_of_hashes.times.map do
people.map {|person| [person, []]}.to_h
end
This code also ensures the arrays and the hashes all occupy their own memory. As can be verified by the following code:
holidays, gifts, *rest = lists
holidays["Bob"] << "Rome"
And checking the values of all the other hashes:
lists
=> [
{"Bob"=>["Rome"], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]}
]
This is my code
lunch_order = {
"Ryan" => "wonton soup",
"Eric" => "hamburger",
"Jimmy" => "sandwich",
"Sasha" => "salad",
"Cole" => "taco"
}
lunch_order.each { |element| puts element }
I want the value to be printed out, but here, both the value and the key are printed.
You can use
lunch_order.each { |key, value| puts value}
Codepad Example
You can loop over the values only with each_value
h = { "a" => 100, "b" => 200 }
h.each_value {|value| puts value }
You can read more about api of hash here.
So many ways to do it in Ruby:
lunch_order.values.each { |element| puts element }
In Ruby a hash contains Keys and Values. So in your case the keys are:
Keys
Ryan
Eric
Jimmy
Sasha
Cole
And the Values for those keys are:
Values
Wonton Soup
Hamburger
Sandwich
Salad
Taco
And all you'd need to do to call the values is use the each loops like you've done but instead of using element as the local variable within the block, you use something like this:
lunch_order.each do { |key, value| puts value }
lunch_order.each_pair { |key,value| puts value }
Docs: http://apidock.com/ruby/Hash/each_pair
Given your original code
lunch_order = {
"Ryan" => "wonton soup",
"Eric" => "hamburger",
"Jimmy" => "sandwich",
"Sasha" => "salad",
"Cole" => "taco"
}
You created a hash, which consists of both keys and values. Keys are on the left, values on the right. To get only the values use
lunch_order.values.each{|value| puts value}
Hope this helps.
I have a hash of people keyed by job and sorted by salary:
person = Struct.new(:salary)
people = {
:butchers => [
person.new(10),
person.new(6),
person.new(4)
],
:bakers => [
person.new(16),
person.new(8),
person.new(7)
],
:candlestick_makers => [
person.new(25),
person.new(21),
person.new(18)
]
}
I want to remove the last x people of each job from their respective array and do something:
def this_example_method
people.each do |job, people|
people.pop(number_for(job)).each do |person|
#do something
end
end
end
the 'do something' works okay, but pop removal doesn't. After running this_example_method, the people hash should look this, but at the moment it's not changing:
people = {
butchers = [
<butcher_1 salary:10>
<butcher_2 salary:6>
],
bakers = [
<baker_1 salary:16>
<baker_2 salary:8>
],
candlestick_makers = [
<candlestick_maker_1 salary:25>
<candlestick_maker_2 salary:21>
]
}
Hash has a shift method that returns the first item and removes it from the hash. If the order matters you could perhaps try to sort it reversed when the hash is created.
Just do as below :
def this_example_method
people.each do |job, persons|
persons.tap { |ob| ob.pop(x) }.each do |person|
#do something
end
end
end
Example :
hash = { :a => [1,2,3], :b => [3,5,7] }
hash.each do |k,v|
v.tap(&:pop).each { |i| # working wit i }
end
hash # => {:a=>[1, 2], :b=>[3, 5]}
I have a Ruby hash which looks like:
{ "id" => "123", "name" => "test" }
I would like to convert it to:
{ :id => "123", :name => "test" }
hash = {"apple" => "banana", "coconut" => "domino"}
Hash[hash.map{ |k, v| [k.to_sym, v] }]
#=> {:apple=>"banana", :coconut=>"domino"}
#mu is too short: Didn't see word "recursive", but if you insist (along with protection against non-existent to_sym, just want to remind that in Ruby 1.8 1.to_sym == nil, so playing with some key types can be misleading):
hash = {"a" => {"b" => "c"}, "d" => "e", Object.new => "g"}
s2s =
lambda do |h|
Hash === h ?
Hash[
h.map do |k, v|
[k.respond_to?(:to_sym) ? k.to_sym : k, s2s[v]]
end
] : h
end
s2s[hash] #=> {:d=>"e", #<Object:0x100396ee8>=>"g", :a=>{:b=>"c"}}
If you happen to be in Rails then you'll have symbolize_keys:
Return a new hash with all keys converted to symbols, as long as they respond to to_sym.
and symbolize_keys! which does the same but operates in-place. So, if you're in Rails, you could:
hash.symbolize_keys!
If you want to recursively symbolize inner hashes then I think you'd have to do it yourself but with something like this:
def symbolize_keys_deep!(h)
h.keys.each do |k|
ks = k.to_sym
h[ks] = h.delete k
symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
end
end
You might want to play with the kind_of? Hash to match your specific circumstances; using respond_to? :keys might make more sense. And if you want to allow for keys that don't understand to_sym, then:
def symbolize_keys_deep!(h)
h.keys.each do |k|
ks = k.respond_to?(:to_sym) ? k.to_sym : k
h[ks] = h.delete k # Preserve order even when k == ks
symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
end
end
Note that h[ks] = h.delete k doesn't change the content of the Hash when k == ks but it will preserve the order when you're using Ruby 1.9+. You could also use the [(key.to_sym rescue key) || key] approach that Rails uses in their symbolize_keys! but I think that's an abuse of the exception handling system.
The second symbolize_keys_deep! turns this:
{ 'a' => 'b', 'c' => { 'd' => { 'e' => 'f' }, 'g' => 'h' }, ['i'] => 'j' }
into this:
{ :a => 'b', :c => { :d => { :e => 'f' }, :g => 'h' }, ['i'] => 'j' }
You could monkey patch either version of symbolize_keys_deep! into Hash if you really wanted to but I generally stay away from monkey patching unless I have very good reasons to do it.
If you are using Rails >= 4 you can use:
hash.deep_symbolize_keys
hash.deep_symbolize_keys!
or
hash.deep_stringify_keys
hash.deep_stringify_keys!
see http://apidock.com/rails/v4.2.1/Hash/deep_symbolize_keys
Just in case you are parsing JSON, from the JSON docs you can add the option to symbolize the keys upon parsing:
hash = JSON.parse(json_data, symbolize_names: true)
Victor Moroz provided a lovely answer for the simple recursive case, but it won't process hashes that are nested within nested arrays:
hash = { "a" => [{ "b" => "c" }] }
s2s[hash] #=> {:a=>[{"b"=>"c"}]}
If you need to support hashes within arrays within hashes, you'll want something more like this:
def recursive_symbolize_keys(h)
case h
when Hash
Hash[
h.map do |k, v|
[ k.respond_to?(:to_sym) ? k.to_sym : k, recursive_symbolize_keys(v) ]
end
]
when Enumerable
h.map { |v| recursive_symbolize_keys(v) }
else
h
end
end
Try this:
hash = {"apple" => "banana", "coconut" => "domino"}
# => {"apple"=>"banana", "coconut"=>"domino"}
hash.tap do |h|
h.keys.each { |k| h[k.to_sym] = h.delete(k) }
end
# => {:apple=>"banana", :coconut=>"domino"}
This iterates over the keys, and for each one, it deletes the stringified key and assigns its value to the symbolized key.
If you're using Rails (or just Active Support):
{ "id" => "123", "name" => "test" }.symbolize_keys
Starting with Ruby 2.5 you can use the transform_key method.
So in your case it would be:
h = { "id" => "123", "name" => "test" }
h.transform_keys!(&:to_sym) #=> {:id=>"123", :name=>"test"}
Note: the same methods are also available on Ruby on Rails.
Here's a Ruby one-liner that is faster than the chosen answer:
hash = {"apple" => "banana", "coconut" => "domino"}
#=> {"apple"=>"banana", "coconut"=>"domino"}
hash.inject({}){|h,(k,v)| h[k.intern] = v; h}
#=> {:apple=>"banana", :coconut=>"domino"}
Benchmark results:
n = 100000
Benchmark.bm do |bm|
bm.report { n.times { hash.inject({}){|h,(k,v)| h[k.intern] = v; h} } }
bm.report { n.times { Hash[hash.map{ |k, v| [k.to_sym, v] }] } }
end
# => user system total real
# => 0.100000 0.000000 0.100000 ( 0.107940)
# => 0.120000 0.010000 0.130000 ( 0.137966)
I'm partial to:
irb
ruby-1.9.2-p290 :001 > hash = {"apple" => "banana", "coconut" => "domino"}
{
"apple" => "banana",
"coconut" => "domino"
}
ruby-1.9.2-p290 :002 > hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h }
{
:apple => "banana",
:coconut => "domino"
}
This works because we're iterating over the hash and building a new one on the fly. It isn't recursive, but you could figure that out from looking at some of the other answers.
hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h }
You can also extend core Hash ruby class placing a /lib/hash.rb file :
class Hash
def symbolize_keys_deep!
new_hash = {}
keys.each do |k|
ks = k.respond_to?(:to_sym) ? k.to_sym : k
if values_at(k).first.kind_of? Hash or values_at(k).first.kind_of? Array
new_hash[ks] = values_at(k).first.send(:symbolize_keys_deep!)
else
new_hash[ks] = values_at(k).first
end
end
new_hash
end
end
If you want to make sure keys of any hash wrapped into arrays inside your parent hash are symbolized, you need to extend also array class creating a "array.rb" file with that code :
class Array
def symbolize_keys_deep!
new_ar = []
self.each do |value|
new_value = value
if value.is_a? Hash or value.is_a? Array
new_value = value.symbolize_keys_deep!
end
new_ar << new_value
end
new_ar
end
end
This allows to call "symbolize_keys_deep!" on any hash variable like this :
myhash.symbolize_keys_deep!
def symbolize_keys(hash)
new={}
hash.map do |key,value|
if value.is_a?(Hash)
value = symbolize_keys(value)
end
new[key.to_sym]=value
end
return new
end
puts symbolize_keys("c"=>{"a"=>2,"k"=>{"e"=>9}})
#{:c=>{:a=>2, :k=>{:e=>9}}}
Here's my two cents,
my version of symbolize_keys_deep! uses the original symbolize_keys! provided by rails and just makes a simple recursive call to Symbolize sub hashes.
def symbolize_keys_deep!(h)
h.symbolize_keys!
h.each do |k, v|
symbolize_keys_deep!(v) if v.is_a? Hash
end
end
Facets' Hash#rekey is also a worth mentioning.
Sample:
require 'facets/hash/rekey'
{ "id" => "123", "name" => "test" }.deep_rekey
=> {:id=>"123", :name=>"test"}
There is also a recursive version:
require 'facets/hash/deep_rekey'
{ "id" => "123", "name" => {"first" => "John", "last" => "Doe" } }.deep_rekey
=> {:id=>"123", :name=>{:first=>"John", :last=>"Doe"}}
Here's a little recursive function to do a deep symbolization of the keys:
def symbolize_keys(hash)
Hash[hash.map{|k,v| v.is_a?(Hash) ? [k.to_sym, symbolize_keys(v)] : [k.to_sym, v] }]
end
I have hash (#post) of hashes where I want to keep the order of the hash's keys in the array (#post_csv_order) and also want to keep the relationship key => value in the array.
I don't know the final number of both #post hashes and key => value elements in the array.
I don't know how to assign the hash in a loop for all elements in the array. One by one #post_csv_order[0][0] => #post_csv_order[0][1] works nicely.
# require 'rubygems'
require 'pp'
#post = {}
forum_id = 123 #only sample values.... to make this sample script work
post_title = "Test post"
#post_csv_order = [
["ForumID" , forum_id],
["Post title", post_title]
]
if #post[forum_id] == nil
#post[forum_id] = {
#post_csv_order[0][0] => #post_csv_order[0][1],
#post_csv_order[1][0] => #post_csv_order[1][1]
##post_csv_order.map {|element| element[0] => element[1]}
##post_csv_order.each_index {|index| #post_csv_order[index][0] => #post_csv_order[index][1] }
}
end
pp #post
desired hash assignment should be like that
{123=>{"Post title"=>"Test post", "ForumID"=>123}}
The best way is to use to_h:
[ [:foo,1],[:bar,2],[:baz,3] ].to_h #=> {:foo => 1, :bar => 2, :baz => 3}
Note: This was introduced in Ruby 2.1.0. For older Ruby, you can use my backports gem and require 'backports/2.1.0/array/to_h', or else use Hash[]:
array = [[:foo,1],[:bar,2],[:baz,3]]
# then
Hash[ array ] #= > {:foo => 1, :bar => 2, :baz => 3}
This is available in Ruby 1.8.7 and later. If you are still using Ruby 1.8.6 you could require "backports/1.8.7/hash/constructor", but you might as well use the to_h backport.
I am not sure I fully understand your question but I guess you want to convert a 2d array in a hash.
So suppose you have an array such as:
array = [[:foo,1],[:bar,2],[:baz,3]]
You can build an hash with:
hash = array.inject({}) {|h,e| h[e[0]] = e[1]; h}
# => {:foo=>1, :bar=>2, :baz=>3}
And you can retrieve the keys in correct order with:
keys = array.inject([]) {|a,e| a << e[0] }
=> [:foo, :bar, :baz]
Is it what you were looking for ?
Answers summary
working code #1
#post[forum_id] = #post_csv_order.inject({}) {|h,e| h[e[0]] = e[1]; h}
working code #2
#post[forum_id] = Hash[*#post_csv_order.flatten]
working code #3
#post[forum_id] ||= Hash[ #post_csv_order ] #requires 'require "backports"'