When trying to pass a block to the sum method:
def sum(list, &block)
list.find_all{yield}.reduce(0, :+)
end
sum([12, 14, 0, 7, 56, 0]) {|i| i % 2 == 0}
I get this error:
NoMethodError: undefined method `%' for nil:NilClass
My method couldn't recognize the i as an element in my list. I don't know how fix it. Any suggestion?
list.find_all { |i| yield i }.reduce(0, :+)
or equivalently
list.find_all(&block).reduce(0, :+)
What you originally wrote searched through the list so that for each element you yield - this would invoke the block with no parameters, i gets assigned nil, and nil % 2 is bad.
Related
I'm trying to call a method I defined earlier in the code and it gives me this error:
"undefined method `filter' for 0:Integer (NoMethodError)"
This is the code:
def descending_order(n)
#arrNum = n.to_s.chars.map {|i| i.to_i }
#final = [#arrNum[0]]
def self.filter(arr, ind)
if self > arr[ind]
#final.insert(ind, self)
else
self.filter(arr, ind+1)
end
end
#arrNum.each { |i| i.filter(#final, 0) }
return #final
end
I tried everything I could think of! Thanks
Your approach is wrong!
If your intention is to return a reverse sorted array of a given number then the code snippet should be:
def descending_order(n)
n.digits.sort.reverse
end
The above snippet returns following output
puts descending_order(352614)
=> [6, 5, 4, 3, 2, 1]
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.
I have:
class Thing
def initialize
#array = [[0, 0, 0], [1, 1, 1]]
end
end
thing = Thing.new
The normal way to access an element in #array is to use [] as in:
#array[0][1] # => 0
I am trying to overwrite [] so as to get results like this:
position_array = [0, 1]
#array[position_array] # => 0
This is my attempt:
class Thing
def [](position_array)
index_row, index_col = position_array
#array[index_row][index_col]
end
def get_value(position_array)
#array[position_array] # doesn't work
# self[position_array] # does work
end
end
thing.get_value([0, 1])
# >> 'get_value': no implicit conversion of Array into Integer (TypeError)
Why do I need to index the Thing object in order to index #array?
Just think of message and receiver.
#array[position_array] sends the message [] to the receiver #array. #array is an instance of Array, so the method Array#[] gets invoked.
self[position_array] sends the message [] to the receiver self. Within instance methods, self refers to that instance. And because self is an instance of Thing, the method Thing#[] gets invoked.
Since Thing is a subclass of Object and not a subclass of Array (nothing wrong here, you shouldn't subclass Array anyway), your implementation of [] does not override Array#[]. Both methods are totally independent of each other, just like String#[] or Hash#[].
This is how I would approach it:
class Thing
def initialize
#array = [[1, 2, 3], [4, 5, 6]]
end
def [](i, j)
#array[i][j]
end
end
thing = Thing.new
thing[0, 1] #=> 2
thing[1, 1] #=> 5
You could use a prepended method to non-invasively override the [] method in Array by duck-typing the parameter passed to the [] method, and then calling the original if its not what you expect. Then you don't need a Thing object at all.
module MyArrayExtension
def [] (*param)
if param.size == 2
row, col = param
raise ArgumentError, 'Row must be an integer' if row.class != Integer
raise ArgumentError, 'Column must be an integer' if col.class != Integer
raise ArgumentError, "Element at row #{row} is not an array" if self[row].class != Array
self[row][col]
else
super
end
end
end
class Array
prepend MyArrayExtension
end
thing = [[1,2,3],[4,5,6]]
puts "The 2D array is: #{thing}"
puts "Extension used on the thing to get at element 1 of first array:"
puts thing[0,1]
puts '-' * 20
normal = [1,2,:blah,4,5]
puts "Normal array is #{normal}"
puts "Original [] method used to get the 3rd element:"
puts normal[2]
puts '-' * 20
puts "Using the extension on the non-2D array:"
puts normal[0,1]
The output of this program is:
The 2D array is: [[1, 2, 3], [4, 5, 6]]
Extension used on the thing to get at element 1 of first array:
2
--------------------
Normal array is [1, 2, :blah, 4, 5]
Original [] method used to get the 3rd element:
blah
--------------------
Using the extension on the non-2D array:
./test.rb:9:in `[]': Element at row 0 is not an array (ArgumentError)
from ./test.rb:35:in `<main>'
def key_for_min_value(name_hash)
name_hash.max_by {|k, v| 0-v}[0]
end
This was my code to fulfill the test suite for finding the lowest value of a hash (this was for one of my lessons online).
I know there are much easier ways to do this but I had some restrictions, as you can see below:
**A Few Restrictions:
We want you to build this on your own. Some of the following methods are helpful but off limits for this exercise. (We'll cover a few below in more depth in subsequent lessons).
I could not use keys, values, min, sort, min_by to make it pass.
This code returned the key with the lowest value (a hash of key ==> integers) but here was the requirement I could not figure out.
If the method is called and passed an argument of an empty hash, it should return nil.
Only first month programming, so this may be obvious but is there a way to return nil for an empty hash, and keep my existing code intact?
Thanks for your help
To a beginner programmer I would recommend to print all intermediate results of expressions, or work in IRB.
def key_for_min_value(name_hash)
puts
puts "in key_for_min_value with parameter #{name_hash}"
# puts "about to return nil" if name_hash.empty?
# return nil if name_hash.empty?
name_hash.max_by { | item | puts "item=#{item}" }
max = name_hash.max_by do | k, v |
puts "k=#{k} v=#{v} 0 - v = #{0 - v}"
0 - v
end
puts "max=#{max.inspect}, class of value returned by max_by : #{max.class}"
result = name_hash.max_by {|k, v| 0-v}[0]
puts "result=#{result.inspect}"
result
end
key_for_min_value({a: 1, b: 2, c: 3})
key_for_min_value({})
Execution :
$ ruby -w t.rb
in key_for_min_value with parameter {:a=>1, :b=>2, :c=>3}
item=[:a, 1]
item=[:b, 2]
item=[:c, 3]
k=a v=1 0 - v = -1
k=b v=2 0 - v = -2
k=c v=3 0 - v = -3
max=[:a, 1], class of value returned by max_by : Array
result=:a
in key_for_min_value with parameter {}
max=nil, class of value returned by max_by : NilClass
t.rb:15:in `key_for_min_value': undefined method `[]' for nil:NilClass (NoMethodError)
from t.rb:21:in `<main>'
The documentation of enum.max_by says :
Returns the item corresponding to the largest value returned by the
block.
But if the enum is empty, it returns nil, from which you fetch element [0], which causes the error because there is no such method in the NilClass.
If you add return nil if name_hash.empty? at the beginning of the method, you prevent it to happen (with two uncommented lines) :
$ ruby -w t.rb
in key_for_min_value with parameter {:a=>1, :b=>2, :c=>3}
...
in key_for_min_value with parameter {}
about to return nil
There a lot of different possibilities to do what you want. The most obvious one is to literally translate the sentence: "return nil if the hash is empty" into Ruby:
def key_for_min_value(name_hash)
return nil if name_hash.empty?
name_hash.max_by {|k, v| 0-v}[0]
end
Another possibility would be to use the safe navigation operator:
def key_for_min_value(name_hash)
name_hash.max_by {|k, v| 0-v}&.[](0)
end
Yet another way would be to ensure that the value you are trying to index into is never nil:
def key_for_min_value(name_hash)
(name_hash.max_by {|k, v| 0-v} || [])[0]
end
# or
def key_for_min_value(name_hash)
Array(name_hash.max_by {|k, v| 0-v})[0]
end
I am implementing the each method on my own. I am supposed to explicitly return self before closing the method. This is my code:
module Enumerable
def my_each
for i in self
yield i
end
#self
end
end
[1,2,3,4].my_each {|x| x + 1} # => [1,2,3,4]
Why does the code still return the receiver even though I did not explicitly return self on the last line?
why does the code still return self even though I did not explicitly return self on the last line?
If you don't specify a return value explicitly, a method will return the last expression evaluated. The last expression in your method is the for loop.
From its documentation:
The result value of a for loop is the value iterated over unless break is used.
Examples:
for i in 1..10
end
#=> 1..10
for i in [1, 2, 3]
end
#=> [1, 2, 3]
for i in [1, 2, 3]
break :foo
end
#=> :foo