Undefined method error when using 'yield' and 'even?' - ruby

When I try to call a method with select and num.even? as follows,
def selection(array)
puts "This is inside the method"
return yield(array)
end
collection = [1,2,3,4,5]
selection(collection.select) { |num| num.even? }
I get a no defined Method error:
undefined method `even?' for #<Enumerator: [1, 2, 3, 4, 5]:select>
I'm looking for a return of even numbers in the array. I can get the select even? combo work in other examples of an array.

Array#select returns an Enumerator instance if no block was given to it
then you call selection method passing the result of call to collection.select as an argument and { |num| num.even? } as block
inside your selection function you yield the argument (an Enumerator instance) to the block
in the block you call even? on the block argument, resulting in the error message you receive.
I am unsure what’s wrong with collection.select(&:even?), but if you want to re-implement it yourself, here you go:
def selection(array)
# convention: return enumerator unless block is given
return enum_for(:selection) unless block_given?
enumerator = array.each
result = []
loop do
(value = enumerator.next) rescue return result
result.push(value) if yield value
end
end
selection([1,2,3,4,5]) { |num| num.even? }
#⇒ [2, 4]

You are seeing this error because you are passing an enumerator object in to your "selection" method ... that is, the result of "collection.select" is an Enumerator and enumerators do not implement an "even" method.
I believe that you are trying to implement your own version of "select". The following is one way to achieve your stated intent: "I'm looking for a return of even numbers in the array."
def selection(array)
results = []
for item in array do
results << item if yield item
end
results
end
collection = [1,2,3,4,5]
puts selection(collection) { |num| num.even? }
# => [2,4]
https://mixandgo.com/learn/mastering-ruby-blocks-in-less-than-5-minutes is a nice reference

yield(array) is passing the whole array in one go to the block given to the method, so it is trying to call even? on the array.

Related

Enumerator without collection

I have run into this construction:
Enumerator.new(&:map)
and couldn't figure out how I can use it.
For sure this is valid code and it returns the enumerator, but it doesn't have any collection to enumerate over.
How to feed a collection to this enumerator?
With the current setup you can't pass any collection to it. You can't change the collection of the enumerator once instantiated.
The current code only works because is block is not instantly executed, therefore you see the error when you try to start iterating (or retrieving items).
enumerator = Enumerator.new(&:map)
enumerator.take(1)
# NoMethodError (undefined method `map' for #<Enumerator::Yielder:0x00000000055b6e90>)
This is because Enumerator::new yields a Enumerator::Yielder which doesn't has the method map.
The above could also be written as:
enumerator = Enumerator.new { |yielder| yielder.map }
If you would like to create an enumerator from a collection the easiest way is to call each without block. Other methods like map also create enumerators without block given.
enumerator = [1, 2, 3].each
#=> #<Enumerator: [1, 2, 3]:each>
If you for some reason still want to create the enumerator by hand it could look like this:
enumerator = Enumerator.new { |yielder| [1, 2, 3].each { |number| yielder << number } }
If the intent was to preselect a method of iterating before you receive the collection, you can do so in the following manner:
# assuming both the collection and block are passed by the user
map = :map.to_proc
result = map.call(collection, &block)
# which is equivalent to
result = collection.map(&block)

How can I modify my ruby method so it takes in a block of code as well?

I have a method called myFilter that takes in an array, and filters out the elements that don't meet the requirement.
For example.
arr = [4,5,8,9,1,3,6]
answer = myfilter(arr) {|i| i>=5}
this run would return an array with elements 5,8,9,6 since they are all greater than or equal to 5.
How would I preform this? the algorithm is easy, but I don't understand how we take in that condition.
Thank you.
I take for granted you don't want to use select method or similar but you want to understand how blocks work.
def my_filter(arr)
if block_given?
result = []
arr.each { |element| result.push(element) if yield element } # here you use the block passed to this method and execute it with the current element using yield
result
else
arr
end
end
The idiomatic way would be:
def my_filter(arr)
return enum_for(:my_filter, arr) unless block_given?
arr.each_with_object([]) do |e, acc|
acc << e if yield e
end
end
More info on Enumerator::Lazy#enum_for.
you can do
def my_filter(arr, &block)
arr.select(&block)
end
then call
my_filter([1, 2, 3]) { |e| e > 2 }
=> [3]
but instead you can just call select with a block directly :)

Trying to write my own all? method in Ruby

There is a method called all? in Enumerable.
I'm trying to learn all the methods of Enumberable's library by writing them myself.
This is what I've come up so far for the all? method. I sorta understand it but I got stumped when trying to pass initialized values to my method.
EDIT for the record, I'm aware that enum method that I have is not the right way ie, it's hard-coded array. This is for self-learning purposes. I'm just trying to figure out how to pass the initialized values to my all? method. That's why I wrote enum in the first place, to see that it is working for sure. Please don't take this class as a literal gospel. Thank you.
class LearningMethods
def initialize(values)
#values = values
end
def enum
array = [10, 3, 5]
end
def all?(a)
yield(a)
end
end
c = LearningMethods.new([10, 3, 5])
p c.enum.all? {|x| x >= 3 } #this works
p c.all?(10) { |x| x >= 3 } #this works
p c.all?(#values) { |x| x >= 3 } #this doesn't work. Why not? And how do I pass the initialized values?
I'm not sure why you need enum at all? Enumerable is a module included in array, so if you're not familiar with this I recommend you read about "modules and mix-ins" in Ruby.
all? works simply by passing EACH of the array elements to the block. If there is ANY element (at least 1) for which the block returns false, then all? evaluates to false. Try analyzing this code:
class MyAllImplementation
def initialize(array)
#array = array
end
def my_all?
#array.each do |element| # for each element of the array
return true unless block_given? # this makes sure our program doesn't crash if we don't give my_all? a block.
true_false = yield(element) # pass that element to the block
return false unless true_false # if for ANY element the block evaluates to false, return false
end
return true # Hooray! The loop which went over each element of our array ended, and none evaluted to false, that means all elements must have been true for the block.
end
end
a = MyAllImplementation.new([1,2,3])
p a.my_all? { |x| x > 0 } #=> true
p a.my_all? { |x| x > 1 } # false, because 1 is not bigger than 1, it's equal to 1

ruby enumerables: is there a detect for results of block evaluation?

I'm looking for something similar to #detect in enumerables, but not quite. This is what enumerable does:
[1, 2, 3].detect {|i| i > 1 } #=> 2
it returns the first instance of the array which matches the condition. Now, my purpose is to return the value of the block. Concern is not exactly the conditions, but for instance, the first which is not nil. Something like this:
[var1, var2, var3].wanted_detect {|var| another_function(var) }
in which the function would return the first result of another_function call which isn't nil.
Mapping the values of applying the method on the variables and then using detect is not an option. This one would ideally have to work in lazy enumerators, for which the early mapping of all possible values is a no-go
[var1, var2, var3].lazy.map { |var| another_function(var) }.reject(&:nil?).first
If you don't have access to Enumerable#lazy, it is easy enough to implement what you want:
module Enumerable
def wanted_detect
self.each do |obj|
val = yield obj
return val if val
end
end
end
Demo:
[1, 2, 3, 4].wanted_detect { |x| x*x if x > 2 }
# => 9
EDIT: Sorry, I missed the last paragraph till falsetru pointed it out.
Thanks for the comments, falsetru.

How to implement an enumerator in Ruby?

For example:
a = [1,2,3,4,5]
a.delete_if { |x| x > 3 }
is equivalent to:
a = [1,2,3,4,5]
a.delete_if.each.each.each.each { |x| x > 3 }
I know a.delete_if returns an enumerator. But how does it know it should delete object when the each block returns true? How to implement delete_if by hand(and in Ruby)?
You can take a look at the Rubinius source code: enumerable module
Here an example of the reject method:
def reject
return to_enum(:reject) unless block_given?
ary = []
each do |o|
ary << o unless yield(o)
end
ary
end
In the implementation of delete_if, the code can verify the value returned from yield to decide whether or not to delete the given entry from the array.
You can read Implementing Iterators in the Programming Ruby guide for more details, but it would looks something like:
class Array
def delete_if
reject { |i| yield i }.to_a
end
end
The above uses yield to pass each item in the array to the block associated with the call to delete_if, and implicitly returns the value of the yield to the outer reject call.

Resources