Ruby hash/sub-hash existance check [duplicate] - ruby

This question already has answers here:
How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?
(16 answers)
Closed 7 years ago.
I find myself needing to put guards like this:
if hash[:foo] && hash[:foo][:bar] && hash[:foo][:bar][:baz]
puts hash[:foo][:bar][:baz]
end
I'd like to shorten this in some way; I know I can wrap in a begin/rescue block but that seems worse. Maybe something like:
ruby Hash include another hash, deep check

Something like:
def follow_hash(hash, path)
path.inject(hash) { |accum, el| accum && accum[el] }
end
value = follow_hash(hash, [:foo, :bar, :baz])
puts value if value

I found this article very informative: http://avdi.org/devblog/2011/06/28/do-or-do-not-there-is-no-try/
value = Maybe(params)[:foo][:bar][:baz][:buz]

Related

Why doesn't my ruby method call work? (yield) [duplicate]

This question already has answers here:
Passing block into a method - Ruby [duplicate]
(2 answers)
Closed 2 years ago.
I can't figure out why I get this error message when I run my file on the console: no block given (yield) (LocalJumpError)
Here my code:
def block_splitter(array)
array.partition { |item| yield(item) }
end
beatles = ["John", "Paul", "Ringo", "George"]
puts block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end
Thanks for your help!
It's a whitespace issue. Your problem is in this line:
puts block_splitter(beatles) do |beatle|
# ...
end
The above code is being interpreted like this:
puts(block_splitter(beatles)) do |beatle|
# ...
end
I.e. the ruby interpreter thinks that the block is being passed to the puts method, not the block_splitter method.
By assigning a variable and printing the result, you'll see that this works as expected:
result = block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end
puts result
Or, you can define this as a 1-liner, and the ruby interpreter handles it like you expected:
puts block_splitter(beatles) { |beatle| beatle.start_with?("P") }
Or, you could wrap it in extra brackets:
puts(block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end)
So there is a problem with missing parentheses. Ruby interpreter allow not to use those, but when You use nested method calls It's better (and sometimes necessary) to use them. To fix it You can do something like this
puts(block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end)
Or even better
puts(block_splitter(beatles) {|beatle| beatle.start_with?("P")})

in ruby, when is a block not a block? [duplicate]

This question already has an answer here:
Ruby block and unparenthesized arguments
(1 answer)
Closed 7 years ago.
Jokes aside, I have a strange situation, I have some code:
def remotes(form,remotes)
personalised_form = form.dup
remotes.each do |ident,remote|
object = yield(ident)
result = remote.call(object)
insert_into_(personalised_form,ident,result)
end
personalised_form
end
And I'm seeing if it works like so:
pp remotes(forms,remotes) do |ident|
case(ident)
when :get_assets
'#Userobject'
end
end
The problem is that ruby seems to think I'm not passing a block to the remotes function.
Why is ruby insisting that I'm not passing a block? (it gives a no block given (yield) (LocalJumpError) specifically).
Thought it's not relevant, remotes is a hash containing key's and Procs, and form is just a specificly structured hash that has the result of the proc inserted into it using the ident to locate the correct insertion point
Ruby thinks you are passing the block to pp method, which simply ignores it. Try:
res = remotes(forms,remotes) do |ident|
case(ident)
when :get_assets
'#Userobject'
end
end
pp res

What does "||=" mean in Ruby? [duplicate]

This question already has answers here:
What does ||= (or-equals) mean in Ruby?
(23 answers)
Closed 7 years ago.
I'm still pretty green when it comes to Ruby and am trying to figure out what this is doing:
command_windows.each {|window| window.hidden ||= window.open? }
The command_windows variable appears to be an array of objects. If someone could explain to me what this line of code means, particularly what the ||= symbol is I would appreciate it.
foo ||= "bar" is the equivalent of doing foo || foo = "bar".
As Mischa explained, it checks for a falsy value before assigning.
In your case, you could think of it as:
command_windows.each {|window| window.hidden || window.hidden = window.open? }
which is another way of saying
command_windows.each {|window| window.hidden = window.open? unless window.hidden }
The ||= operator is used to assign new value to variable. If something was assigned to it before it won't work. It is usually used in hashes, so you don't have to check, if something is already assigned.

what ruby tap method does on a {} [duplicate]

This question already has answers here:
Understanding tap in Ruby
(2 answers)
Closed 8 years ago.
I've read about what tap does in Ruby but I'm confused by the code block below,
{}.tap do |h|
# some hash processing
end
any help would be greatly appreciated.
#tap method simply passes an object it was called on to a block. At the end of the block it returns the same object again. This way you can chain operations or restrict variable scope.
{}.tap { |h| h[:a] = 1 }.size # => 1
You were able to chain a next method to this block. And also avoided creating a h variable in your scope.
tap is particularyl useful if you want to do some modifications on the hash and after that return it, e.g. inside a method that returns the hash. The advantage of tap being that you don't have to explitely return the hash at the end and there's no need for an intermediary variable.
hash = {}
hash[:a] = 'b'
hash
# => {:a=>"b"}
vs.
{}.tap do |hash|
hash[:a] = 'b'
end
# => {:a=>"b"}
For exemple : you want to chain delete and each on a hash.
You cannot do
hash.delete(:key).each {...}
but can do
hash.tap { |h| h.delete(:key) }.each { ... }
It is a good way to chain your methods.
tap doesn't do anything to it. Whatever you do to h in the block is whatever is done to the original {}.

Ruby - Access multidimensional hash and avoid access nil object [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Ruby: Nils in an IF statement
Is there a clean way to avoid calling a method on nil in a nested params hash?
Let's say I try to access a hash like this:
my_hash['key1']['key2']['key3']
This is nice if key1, key2 and key3 exist in the hash(es), but what if, for example key1 doesn't exist?
Then I would get NoMethodError: undefined method [] for nil:NilClass. And nobody likes that.
So far I deal with this doing a conditional like:
if my_hash['key1'] && my_hash['key1']['key2'] ...
Is this appropriate, is there any other Rubiest way of doing so?
There are many approaches to this.
If you use Ruby 2.3 or above, you can use dig
my_hash.dig('key1', 'key2', 'key3')
Plenty of folks stick to plain ruby and chain the && guard tests.
You could use stdlib Hash#fetch too:
my_hash.fetch('key1', {}).fetch('key2', {}).fetch('key3', nil)
Some like chaining ActiveSupport's #try method.
my_hash.try(:[], 'key1').try(:[], 'key2').try(:[], 'key3')
Others use andand
myhash['key1'].andand['key2'].andand['key3']
Some people think egocentric nils are a good idea (though someone might hunt you down and torture you if they found you do this).
class NilClass
def method_missing(*args); nil; end
end
my_hash['key1']['key2']['key3']
You could use Enumerable#reduce (or alias inject).
['key1','key2','key3'].reduce(my_hash) {|m,k| m && m[k] }
Or perhaps extend Hash or just your target hash object with a nested lookup method
module NestedHashLookup
def nest *keys
keys.reduce(self) {|m,k| m && m[k] }
end
end
my_hash.extend(NestedHashLookup)
my_hash.nest 'key1', 'key2', 'key3'
Oh, and how could we forget the maybe monad?
Maybe.new(my_hash)['key1']['key2']['key3']
You could also use Object#andand.
my_hash['key1'].andand['key2'].andand['key3']
Conditions my_hash['key1'] && my_hash['key1']['key2'] don't feel DRY.
Alternatives:
1) autovivification magic. From that post:
def autovivifying_hash
Hash.new {|ht,k| ht[k] = autovivifying_hash}
end
Then, with your example:
my_hash = autovivifying_hash
my_hash['key1']['key2']['key3']
It's similar to the Hash.fetch approach in that both operate with new hashes as default values, but this moves details to the creation time.
Admittedly, this is a bit of cheating: it will never return 'nil' just an empty hash, which is created on the fly. Depending on your use case, this could be wasteful.
2) Abstract away the data structure with its lookup mechanism, and handle the non-found case behind the scenes. A simplistic example:
def lookup(model, key, *rest)
v = model[key]
if rest.empty?
v
else
v && lookup(v, *rest)
end
end
#####
lookup(my_hash, 'key1', 'key2', 'key3')
=> nil or value
3) If you feel monadic you can take a look at this, Maybe

Resources