Reputation: 2246
Is there a construction in RoR that would allow me to write this:
@budget_project_amounts.detect {|attributes| attributes['id'] == 659 }.attributes['amount'] if !@budget_project_amounts.detect {|attributes| attributes['id'] == 659 }.nil?
more succinctly? Without the use of a variable...
Edit: This part
@budget_project_amounts.detect {|attributes| attributes['id'] == 659 }
is irrelevant. It could be anything. I sometimes have a situation like this one, where I first need to check if something is nil, before being able to continue to get a value under it (in case it's not nil and I'm looking for a way to do this without having to repeat the first part completely. I hope this makes sense.
Upvotes: 2
Views: 61
Reputation: 67910
You can write it compactly using the abstraction map_detect:
require 'facets/enumerable/map_detect'
amount = amounts.map_detect { |attrs| attrs['amount'] if attrs['id'] == 659 }
Upvotes: 0
Reputation: 8295
You can use a simple
@budget_project_amount.
select{ |x| x[:id] == 1 && x[:amount].present?}.
map{|x| x[:amount] }.
first
It will return the first amount
or nil
PS I guess that ids are unique...
Upvotes: 0
Reputation: 3706
You could try with short circuits, for instance (to mention your example):
(bp = @budget_project_amount.find { |b| b['id'] == 12 }) && bp['amount']
The result of the expression is either nil or the amount you are searching for. Less than this I cannot think of anything.
It's called short-circuit because if the first expression evaluates to false, than the second one is not evaluated, avoiding calling the method on a nil object.
Upvotes: 2
Reputation: 18037
Avoiding the use of a variable... you can start by trying to eliminate the if
condition at the end by using try
:
@budget_project_amount.detect {|attributes| attributes['id'] == 659 }
.try(attributes).try(:[], 'amount')
However, just because this is less code doesn't really make it better code. The problem with your original approach and this approach is that both rely on type checking (for NilClass) to get the job done. And type checking is a code smell.
I like to get around type checking by using Naught. With the right null object setup you can do this instead:
Maybe(@budget_project_amount.detect {|attributes| attributes['id'] == 659 }).attributes[:amount].to_f
Basically, forget about type checking, just do what you wanted to do and make sure the end result is the type of object you're looking for (with .to_f
or .to_i
or whatever).
For the above example to work, I define my null objects like this:
require 'naught'
NullObject = Naught.build do |config|
config.define_explicit_conversions
config.define_implicit_conversions
config.black_hole
if $DEBUG
config.traceable
else
config.singleton
end
end
This provides explicit conversions (for .to_f
) and is a black hole (which allows multiple methods to be called in a chain regardless of where a nil appears in that chain).
Upvotes: 3