Ruby method call, element.send(), throws ArgumentError - ruby

When I run this Ruby code I get an ArgumentError: wrong number of arguments calling ``method`` (0 for 1).
def method(element)
return element + 2
end
array = Array.[](1,2,3,4,5)
def map(array, method)
result_array = []
array.each do |element|
# Call the method on the object
value = element.send(method)
# Add to array
result_array.push(value)
end
return result_array
end
map(array, :method)
Calling the method this way works.
value = method(element)
What is wrong with the element.send(method) syntax?

With send you need to pass a method argument value = send(method, element). To call it on element is unnecessary. By the way, Ruby already has a method called method it is better to not override it. Rename it to something more meaningful like add_two.

Calling the method this way works.
value = method(element)
What is wrong with the element.send(method) syntax?
In your example, element is an integer, and method is a symbol, e.g. 1 and :method.
So value = method(element) is equivalent to:
value = method(1)
whereas value = element.send(method) is equivalent to:
value = 1.send(:method)
which is basically:
value = 1.method
It should be obvious that method(1) and 1.method is not the same.

Related

Attempting to use an array of Procs by using #.each on the Array but getting Error

We have to write a method, chain_map, that accepts any value and an array of procs as an argument. The method should return the final result of feeding the value through all of the procs. For example, if the array contains three procs, then:
-the value is given to the first proc
-the result of the first proc is given to the second proc
the result of the second proc is given to the third proc
the result of third proc is the final result
I wrote:
def chain_map(val, *prcs)
new_val = val
prcs.each do |prc|
new_val = prc.call(new_val)
end
new_val
end
When I run the code with the following:
add_5 = Proc.new { |n| n + 5 }
half = Proc.new { |n| n / 2.0 }
p chain_map(25, [add_5, half]) # 15.0
I get the following error on the line where it says new_val = prc.call(new_val):
undefined method `call' for #Array:0x00007fffd40268c8 (NoMethodError)
When I am using .each, its iterating over each proc stored in the array(prcs), so I don't see why it is giving an error.
Any help is appreciated.
chain_map's second argument defined with splat operator, that mean remain arguments after val argument given by the caller will be wrapped into array.
But because you passing array of the procs as single argument chain_map(25, [add_5, half]), given array will be wrapped with another array in the method.
Don't wrap procs with the array
chain_map(25, add_5, half)
Or use splat operator on the array of procs if you build procs dynamically
chain_map(25, *procs)
Alternative approach by using reduce
result = procs.reduce(25) { |value, proc| proc.call(value) }

How to chain a method call to a `do ... end` block in Ruby?

I'm doing the following:
array_variable = collection.map do |param|
some value with param
end
return array_variable.compact
Can I call map and compact in one statement somehow, so I can return the result instantly?
I'm thinking on something like this (it may be invalid, however):
array_variable = block_code param.compact
# block_code here is a method for example which fills the array
yes, you can call a method here.
In your case,
array_variable = collection.map do |param|
# some value with param
end.compact
OR
array_variable = collection.map{ |param| some value with param }.compact
As pointed out by #Stefan, assignment is not required, you can directly use return and if that's the last line of method you can omit return too..

Yield within Set to eliminate in an Array

I found the following code here for eliminating duplicate records in an array:
require 'set'
class Array
def uniq_by
seen = Set.new
select{ |x| seen.add?( yield( x ) ) }
end
end
And we can use the code above as follows:
#messages = Messages.all.uniq_by { |h| h.body }
I would like to know how and what happens when the method is called. Can someone explain the internals of the code above? In the uniq_by method, we did not do anything to handle block argument. How is the passed argument handled by uniq_by method?
Let's break it down :
seen = Set.new
Create an empty set
select{ |x| seen.add?( yield( x ) ) }
Array#select will keep elements when the block yields true.
seen.add?(yield(x)) will return true if the result of the block can be added in the set, or false if it can't.
Indeed, yield(x) will call the block passed to the uniq_by method, and pass x as an argument.
In our case, since our block is { |h| h.body }, it would be the same as calling seen.add?(x.body)
Since a set is unique, calling add? when the element already exists will return false.
So it will try to call .body on each element of the array and add it in a set, keeping elements where the adding was possible.
The method uniq_by accepts a block argument. This allows to specify, by what criteria you wish to identify two elements as "unique".
The yield statement will evaluate the value of the given block for the element and return the value of the elements body attribute.
So, if you call unique_by like above, you are stating that the attribute body of the elements has to be unique for the element to be unique.
To answer the more specific question you have: yield will call the passed block {|h| h.body} like a method, substituting h for the current x and therefore return x.body
In Ruby, when you are putting yield keyword inside any method(say #bar), you are explicitly telling #bar that, you will be using a block with the method #bar. So yield knows, inside the method block will be converted to a Proc object, and yield have to call that Proc object.
Example :
def bar
yield
end
p bar { "hello" } # "hello"
p bar # bar': no block given (yield) (LocalJumpError)
In the uniq_by method, we did not do anything to handle block argument. How is the passed argument handled by uniq_by method?
You did do, that is you put yield. Once you will put this yield, now method is very smart to know, what it supposed to so. In the line Messages.all.uniq_by { |h| h.body } you are passing a block { |h| h.body }, and inside the method definition of uniq_by, that block has been converted to a Proc object, and yield does Proc#call.
Proof:
def bar
p block_given? # true
yield
end
bar { "hello" } # "hello"
Better for understanding :
class Array
def uniq_by
seen = Set.new
select{ |x| seen.add?( yield( x ) ) }
end
end
is same as
class Array
def uniq_by
seen = Set.new
# Below you are telling uniq_by, you will be using a block with it
# by using `yield`.
select{ |x| var = yield(x); seen.add?(var) }
end
end
Read the doc of yield
Called from inside a method body, yields control to the code block (if any) supplied as part of the method call. If no code block has been supplied, calling yield raises an exception. yield can take an argument; any values thus yielded are bound to the block's parameters. The value of a call to yield is the value of the executed code block.
Array#select returns a new array containing all elements of the array for which the given block returns a true value.
The block argument of the select use Set#add? to determine whether the element is already there. add? returns nil if there is already the same element in the set, otherwise it returns the set itself and add the element to the set.
The block again pass the argument (an element of the array) to another block (the block passed to the uniq_by) using yield; Return value of the yield is return value of the block ({|h| h.body })
The select .. statement is basically similar to following statement:
select{ |x| seen.add?(x.body) }
But by using yield, the code avoid hard-coding of .body, and defers decision to the block.

How do I fix the wrong number of brackets in a generated array?

When I call next() I have an extraneous [] in #current. How can I fix this?
class Result
attr_accessor :current
def initialize(*previous)
#current = previous
p #current
end
def next()
res = Result.new(#current)
end
end
res = Result.new([2,2], [3,3], [4,4])
nextArray = res.next
Your first call has 3 parameters, whereas the call in next() has only one.
try:
def next()
res = Result.new(*#current)
end
You'll need to do Result.new(*#current) with an asterisk before the previous, so the array gets "splatted" back into a list of arguments, so you're calling Result.new with three arguments rather than one array containing three arrays.
It's cause *previous is an array. So if you call Result.new #current it's wrapped in next array and so on.
Try expanding the array in #current as separate arguments to the constructor (instead of as a single array argument):
def next
res = Result.new(*#current)
end
See also this question explaining that asterisk operator: What does the (unary) * operator do in this Ruby code?

'pass parameter by reference' in Ruby?

In Ruby, is it possible to pass by reference a parameter with value-type semantics (e.g. a Fixnum)?
I'm looking for something similar to C#'s 'ref' keyword.
Example:
def func(x)
x += 1
end
a = 5
func(a) #this should be something like func(ref a)
puts a #should read '6'
Btw. I know I could just use:
a = func(a)
You can accomplish this by explicitly passing in the current binding:
def func(x, bdg)
eval "#{x} += 1", bdg
end
a = 5
func(:a, binding)
puts a # => 6
Ruby doesn't support "pass by reference" at all. Everything is an object and the references to those objects are always passed by value. Actually, in your example you are passing a copy of the reference to the Fixnum Object by value.
The problem with the your code is, that x += 1 doesn't modify the passed Fixnum Object but instead creates a completely new and independent object.
I think, Java programmers would call Fixnum objects immutable.
In Ruby you can't pass parameters by reference. For your example, you would have to return the new value and assign it to the variable a or create a new class that contains the value and pass an instance of this class around. Example:
class Container
attr_accessor :value
def initialize value
#value = value
end
end
def func(x)
x.value += 1
end
a = Container.new(5)
func(a)
puts a.value
You can try following trick:
def func(x)
x[0] += 1
end
a = [5]
func(a) #this should be something like func(ref a)
puts a[0] #should read '6'
http://ruby-doc.org/core-2.1.5/Fixnum.html
Fixnum objects have immediate value. This means that when they are assigned or
passed as parameters, the actual object is passed, rather than a reference to
that object.
Also Ruby is pass by value.
However, it seems that composite objects, like hashes, are passed by reference:
fp = {}
def changeit(par)
par[:abc] = 'cde'
end
changeit(fp)
p fp
gives
{:abc=>"cde"}

Resources