How to concatenate Ruby conditions dynamically with &&? - ruby

Is there a way to concatenate conditions dynamically with an && operator?
I've got this:
def condition(name)
if Profile.reflect_on_association(name)
send(name).present? && send(name).persisted?
else
send(name).present?
end
end
I tried to build the conditions inside an array and then join them with && but that didn't work of course.
Thanks for any pointers.

I'm a bit unclear on the wording of your original question -- are you looking for a way to combine send(name).present? && send(name).persisted??
One option is this: %i[present? persisted?].all? { |condition| send(name).send(condition) }
Though if you only have a few enumerated conditions, I think the metaprogramming is worse for readability and your code as written in the original question is probably better.

Well, if what you call your "conditions" is an array of values or function resulting in truthy/fasly values, you can actually evaluate them directly with all?:
conditions_1 = [1, 2, nil]
conditions_1.all? # => false
conditions_2 = [1, true, "lala"]
conditions_2.all? # => true
'all?' evaluates if all the items in the array respect a condition - without params it will just evaluate if they are all true. While not exactly what you asked for but it should lead to the same result.

Related

Ruby array contains types?

I need a function to check if an array contains other arrays, or more generally if an array contains a certain class. My naive first approach is:
found=false
[1,"a",[],:asdf].each { |x| found=(x.is_a? Array) ? true : found }
puts found
Any way to optimize this?
In newer Rubies (2.5+) you don't need to pass a block to any?. That links goes to the latest docs, which has a similar example checking for integers.
Therefore, you can simply use:
[1, "a", [], :asdf].any?(Array)
# => true
[1, "a", :asdf].any?(Array)
# => false
That seems the simplest syntax and most sensible approach here if your Ruby version allows it :)
You can try #any?
For example:
[1,"a",[],:asdf].any? { |el| el.is_a? Array }
#=> true
Almost always when you find yourself using each, you are missing some higher-level abstraction.
In this case, that abstraction is the method Array#any?, which will return true if any element of the array satisfies the predicate:
[1, "a", [], :asdf].any?(Array)
this approach iterate over all elements even if you found an array.
in your test case it will iterate over :asdf,
and you don't have to check this.
I think you need to break from loop if you found the certain type(Array in our Example).
found = false
[1,"a",[],:asdf].each do |x|
if x.is_a? Array
found = true
break
end
end
puts found
Also you can use any instead of each
found = [1,"a",[],:asdf].any? { |x| x.is_a? Array }
puts found # true

How to fix end-of-input with two Boolean statements

I'm filtering some Array elements in Ruby and want to filter the positive ones, only if they're all Integers.
l = [1,2,'a','b']
l.select do |number|
new_array = []
new_array.push(number) if number.positive? && number.kind_of? Integer
end
but I got a Syntax error asking for expecting end-of-input.
Why doesn't number.positive? && number.kind_of? Integer work?
The select method is a filtering method and it needs a boolean value that describes if the element should or shouldn't be in the resulting output. In that block you should focus on one thing and one thing only: Describing in minimal terms the acceptance criteria for the filter.
What you're doing here is declaring a local variable, conditionally populating it, then throwing it away, and also discarding the resulting output.
What you really want is to strip this down to the basics:
l = [1,2,'a','b']
filtered = l.select do |number|
number.positive? && number.kind_of? Integer
end
Don't forget to capture the result of this operation or it goes straight in the trash.
There's still a bug here because strings don't have a positive? method, so just reverse the order:
filtered = l.select do |number|
number.kind_of?(Integer) && number.positive?
end
That requires adding brackets to the kind_of? call to avoid ambiguity. && is a very aggressive operator and will interpret the call as this if not properly contained:
number.kind_of?(Integer && number.positive?)
Surround it in parentheses:
new_array.push(number) if number.positive? && (number.kind_of? Integer)
Which results in undefined method `positive?' for "a":String because positive? isn't defined for the strings 'a' and 'b'.
Checking the type first works:
new_array.push(number) if (number.kind_of? Integer) && number.positive?
Just out of curiosity, Monkey patching Object class:
module MyPatches
def integer_and_positive?
kind_of?(Integer) && self > 0
end
end
Object.include MyPatches
For using this way, with Array#keep_if:
l.keep_if(&:integer_and_positive?)
#=> [1, 2]
Note: it alters the original array, for avoiding it use Array#select.

Why does .all? return true on an empty array?

Using Ruby I want to evaluate all items in an array, and return true if they all pass a conditional test.
I can do this using e.g. array.all? { |value| value == 2 }
So:
> array=[2,2]
> array.all? { |value| value == 2 }
=> true
> array=[2,3]
> array.all? { |value| value == 2 }
=> false
Great!
But, why does an empty array pass this test?
> array=[]
> array.all? { |value| value == 2 }
=> true
Shouldn't this return false?
And if I need it to return false, how should I modify the method?
This is a vacuous truth. It's the standard interpretation of a universal quantification, i.e. a
collection.all? { |x| some_predicate(x) }
over an empty collection, but it's known to strike people as counter-intuitive when they first see it in a formal setting. One nice way to think about why this is the preferred semantics is to think about how you would implement all?.
To make your test require that the array is non-empty, just do
array.any? && array.all? { |x| x == 2 }
Note that array.any? is fast no matter how large the array, whereas array.all? { |x| x == 2 } can be slow, depending on how big array is and how rare 2 is in it. So put the array.any? first.
Also note, there are degenerate cases where this won't work, for instance if array is [nil] or [false]. If cases like this might come up, replace array.any? with array.any? { true }.
In Ruby you can never loop over an empty collection (array, hashes, etc.), so in your case your block never gets executed. And if the block never gets executed, all? returns true (there is no condition to make the result false).
Read about all? in the Ruby documentation.
You can simply achieve your goal by
!array.empty? && array.all? { |value| value == 2 }
The documentation says : "The method returns true if the block never returns false or nil.."
In the case of an empty array the block never executes and hence the method will always return true. As far as returning false is concerned you'll have to arr.empty?
There is no item in that array that doesn't pass the test. I think you may need to throw in a test for array length.
Just go
!(array.empty? || array.any? {|x| x != 2})
(Which has the added advantage of failing fast—that is, it can be evaluated properly without having to scan the whole array.)
Since there is no item in the array that FAILS that test, it returns true. So just use somehting like:
array.size > 0 and array.all? { |value| value == 2}
Or something like that.
Zeroes, empty collections, empty matrices and such have always been a bit special, if not outright problematic. Greeks knew well why they didn't count 0 among natural integers.
Method all? would be the first to ask you "why are you calling me on an empty array?" What do you mean by "all?", when there is nothing in there? That's a contradiction. And the method does short thinking, and answers true for the reasons outlined in the other three answers. Remember, you are at fault for talking about "all elements" of an empty array to begin with.
As Amit Kumar Gupta writes, it is the standard interpretation of universal quantification. I have no idea why you expect it to be false. Here, you can see it should be true by inference.
Universal quantification is equivalent to conjunction, thus ("<=>" means equivalent):
"for all x in [a, b, c], P(x)" <=> "P(a) and P(b) and P(c)"
Notice that any proposition is equivalent to the conjunction of true and itself, so:
"for all x in [a, b, c], P(x)" <=> "true and P(a) and P(b) and P(c)"
If you lessen the elements in the set to two, you get:
"for all x in [a, b], P(x)" <=> "true and P(a) and P(b)"
and further to one element:
"for all x in [a], P(x)" <=> "true and P(a)"
Now, what happens with the empty set? Naturally,
"for all x in [], P(x)" <=> "true"
By noticing that existential quantification is equivalent to disjunction, you can also see that you should expect false with existential quantification over an empty set.
The source of all? method says that it uses static variable(which is initially set to true) and then performs the AND operation between the static variable value and the result of the iteration finally returns this static variable as a result.
as the array is Empty ruby will never iterate on this empty array and as a result of this all? method will return the static variable which was set to true.
Make sure the array is not empty first.
Then:
array.compact.present? && array.all? {|x| x != 2}

Evaluate many boolean expressions like Array#join in Ruby

In Ruby, you can use Array#join to simple join together multiple strings with an optional delimiter.
[ "a", "b", "c" ].join #=> "abc"
[ "a", "b", "c" ].join("-") #=> "a-b-c"
I'm wondering if there is nice syntactic sugar to do something similar with a bunch of boolean expressions. For example, I need to && a bunch of expressions together. However, which expressions will be used is determined by user input. So instead of doing a bunch of
cumulative_value &&= expression[:a] if user[:input][:a]
I want to collect all the expressions first based on the input, then && them all together in one fell swoop. Something like:
be1 = x > y
be2 = Proc.new {|string, regex| string =~ regex}
be3 = z < 5 && my_object.is_valid?
[be1,be2.call("abc",/*bc/),be3].eval_join(&&)
Is there any such device in Ruby by default? I just want some syntatic sugar to make the code cleaner if possible.
Try Array#all?. If arr is an Array of booleans, this works by itself:
arr.all?
will return true if every element in arr is true, or false otherwise.
You can use Array#any? in the same manner for joining the array on ||, that is, it returns true if any element in the array is true and false otherwise.
This will also work if arr is an array of Procs, as long as you make sure to pass the correct variables to Proc#call in the block (or use class, instance, or global variables).
You can use #all?, #any? and #none? to achieve this:
[true, false].any?
=> true
[true, false].all?
=> false
And don't forget, that all values other than nul and false evaluate to true.
['', [], {}].all?
=> true
In this particular case, you just want to filter your expressions hash by those which the user has selected, and then test if all those expressions are truthy:
cumulative_value = expression.select {|k, v| user[:input][k] }.values.all?
This will first select all members from expression for which there is a matching user[:input] key, then it will use Array#all? to test if all the values from the selected expressions are truthy. Your cumulative_value is then either true or false.
Since your expression values may be procs, you would then have to evaluate all procs from the filtered expression list, and build a results array from that, which you can call all? on:
cumulative_value = expression.select {|k, v| user[:input][k] }.
values.map {|value| value.is_a?(Proc) ? value.call : value }.all?
Hardly "syntactic sugar", but it gets the job done without being horribly complex.

Is there a simple way to evaluate a value to true/false without using an expression?

Is there a simple way in Ruby to get a true/false value from something without explicitly evaluating it to true or false
e.g. how would one more succinctly express
class User
def completed_initialization?
initialization_completed == 1 ? true : false
end
end
is there some way to do something along the lines of
initialization_completed.true?
There's obviously not much in it but since I'm in the zen garden of Ruby I might as well embrace it
EDIT (I've updated the example)
This question was extremely badly phrased as was very gently pointed out by #Sergio Tulentsev. The original example (below) does of course evaluate directly to a boolean. I'm still struggling to find an example of what I mean however Sergio's double-negative was in fact exactly what I was looking for.
Original example
class User
def top_responder
responses.count > 10 ? true : false
end
end
> operator already returns boolean value. So it can be just
def top_responder
responses.count > 10
end
To convert arbitrary values to booleans, I offer you this little double-negation trick.
t = 'foo'
!!t # => true
t = 1
!!t # => true
t = 0
!!t # => true
t = nil
!!t # => false
The first negation "casts" value to boolean and inverts it. That is, it will return true for nil / false and false for everything else. We need another negation to make it produce "normal" values.

Resources