Benefits of using `Hash#fetch` over `Hash#[]`
Three main uses:
When the value is mandatory, i.e. there is no default:
options.fetch(:repeat).times{...}
You get a nice error message too:
key not found: :repeat
When the value can be
nil
orfalse
and the default is something else:if (doit = options.fetch(:repeat, 1))
doit.times{...}
else
# options[:repeat] is set to nil or false, do something else maybe
endWhen you don't want to use the
default
/default_proc
of a hash:options = Hash.new(42)
options[:foo] || :default # => 42
options.fetch(:foo, :default) # => :default
fetch vs. [] when working with hashes?
By default, using #[]
will retrieve the hash value if it exists, and return nil if it doesn't exist *.
Using #fetch
gives you a few options (see the docs on #fetch):
fetch(key_name)
: get the value if the key exists, raise aKeyError
if it doesn'tfetch(key_name, default_value)
: get the value if the key exists, returndefault_value
otherwisefetch(key_name) { |key| "default" }
: get the value if the key exists, otherwise run the supplied block and return the value.
Each one should be used as the situation requires, but #fetch
is very feature-rich and can handle many cases depending on how it's used. For that reason I tend to prefer it over accessing keys with #[]
.
* As Marc-André Lafortune said, accessing a key with #[]
will call #default_proc
if it exists, or else return #default
, which defaults to nil
. See the doc entry for ::new
for more information.
Are there benefits to returning a tapped hash over an immediate hash?
Using tap
, you could use conditionals (or more complex logic):
def some_method
{}.tap do |data|
data[:foo] = something if condition
data[:bar] = something_else unless condition
end
end
But even in this case, I'd probably avoid tap
and write:
def some_method
data = {}
data[:foo] = something if condition
data[:bar] = something_else unless condition
data
end
How do I use the fetch method for nested hash?
EDIT: there is a built-in way now, see this answer.
There is no built-in method that I know of. I have this in my current project
class Hash
def fetch_path(*parts)
parts.reduce(self) do |memo, key|
memo[key.to_s] if memo
end
end
end
# usage
hash.fetch_path('name', 'Mike', 'age')
You can easily modify it to use #fetch
instead of #[]
(if you so wish).
Whenever X is true do X, otherwise Y
How about a ternary operator?
@transactionparams = {:thing => "foo"}
puts @transactionparams ? @transactionparams[:thing] : 0
@transactionparams = nil
puts @transactionparams ? @transactionparams[:thing] : 0
Why ever use an array instead of a hash?
Retrieving from hash is faster in a sense that you can fetch value directly by key instead of iterating over whole hash (or array when you're searching for particular string). Having that said, $hash{key}
isn't faster than $array[0]
as no iteration is taking place.
Arrays can't be replaced by hashes, as they have different features,
arrays hashes
------------------------------------
ordered keys x -
push/pop x -
suitable for looping x -
named keys - x
Passing hashes instead of method parameters
Both approaches have their own advantages and disadvantages, when you use an options hash replacing standard arguments you lose clarity in the code defining the method but gain clarity whenever you use the method because of the pseudo-named paramaters created by using an options hash.
My general rule is if you either have a lot of arguments for a method (more than 3 or 4) or lots of optional arguments then use an options hash otherwise use standard arguments. However when using an options hash it is important to always include a comment with the method definition describing the possible arguments.
How to change Hash values?
my_hash.each { |k, v| my_hash[k] = v.upcase }
or, if you'd prefer to do it non-destructively, and return a new hash instead of modifying my_hash
:
a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h }
This last version has the added benefit that you could transform the keys too.
Related Topics
Vim Syntax Highlighting for Ruby 1.9
How to Do Complex Querying with Logical Operations by Using Searchkick
Using Each_With_Index with Map
Why Is Foreman Gem Ignoring the Port Environment Variable
Rails 3.1 Rspec Creating Test Case Validate Field for Model
Ruby No Implicit Conversion of Fixnum into String (Typeerror)
How to Print Unicode Character U-1F4A9 'Pile of Poo' Emoji
How to Send Message to All Client Except Sender in Rails/Actioncable
How to Make a Method Available to Both My Controller and Model in Rails
What's the Easiest Way to Send a Message Through Outlook with Ruby
3.Days.Ago, 2.Hours.From_Now etc Without Rails
How to Completely Remove Webpack and All Its Dependencies from Rails App