I Ruby 2 you could do the following:
my_hash = {a: {aa: 1, ab: 2, ac: 3}}
my_hash.each do |key, aa:, ab: 4, **|
puts key
puts aa
puts ab
end
In Ruby 3 this now results in missing keywords :aa, :ab
. What would be the best way to refactor code like this in Ruby 3?
Something like the following would not work because it doesn't support setting default values:
my_hash.each do |key, values|
values in {aa: aa, ab: ab}
end
The best way I can think of is putting the existing code in a wrapper:
lambda = ->(key, aa:, ab: 4, **) do
puts key
puts aa
puts ab
end
my_hash.each do |key, values|
lambda.call(key, **values)
end
Any better options?
I can't think of a way to convert the hash to keyword arguments.
But why not use the hash the way it is, i.e. without treating its keys like keywords? Unless your actual code is more complex, fetch
seems to do what you want:
my_hash = {a: {aa: 1, ab: 2, ac: 3}}
my_hash.each do |key, values|
puts key
puts values.fetch(:aa)
puts values.fetch(:ab, 4)
end
The above works fine in both, Ruby 2 and 3. Just like your example, it will raise an error if :aa
is missing and it will use a default value of 4
if :ab
is missing.
||
As stated by @Stefan it will not works if the value can contains
nil
orfalse
my_hash = {a: {aa: 1, ac: 3}}
my_hash.each do |key, values|
puts key
puts values[:aa]
puts values[:ab] || 4
end
key?
my_hash = {a: {aa: 1, ac: 3}}
default_values_hash = { :ab => 4 }
my_hash.each do |key, values|
puts key
puts values[:aa]
puts values.key?(:ab) ? values[:ab] : default_values_hash[:ab]
end
default
my_hash = {a: {aa: 1, ab: 2, ac: 3}}
my_hash.each do |key, values|
values.default = :notfound
puts key
puts values[:aa]
puts values[:ab] == :notfound ? 4 : values[:ab]
end
my_hash = {a: {aa: 1, ac: 3}}
my_hash.each do |key, values|
values.default = :notfound
puts key
puts values[:aa]
puts values[:ab] == :notfound ? 4 : values[:ab]
end
default-proc
my_hash = {a: {aa: 1, ab: 2, ac: 3}}
default_values_hash = { :ab => 4 }
my_hash.each do |key, values|
values.default_proc = proc { |hash, key| default_values_hash[key] || :notfound }
puts key
puts values[:aa]
puts values[:ab]
end
merge
my_hash = {a: {aa: 1, ac: 3}}
default_values_hash = { :ab => 4 }
my_hash.each do |key, values|
values = default_values_hash.merge(values)
puts key
puts values[:aa]
puts values[:ab]
end
All above code works in both Ruby 2 and 3
Note:
I would suggest the key?
method to test and check if key exists and handle the case accordingly as it would be much faster as comparing to other cases which require additional steps internally.
If you don't need to use default value but need just destruct hash, you can use =>
operator for pattern matching
my_hash = { a: { aa: 1, ab: 2, ac: 3 } }
my_hash.each do |key, values|
values => { aa:, ab:, ac: }
puts key, aa, ab, ac
end
This code will raise NoMatchingPatternKeyError
if values
don't include all keys
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With