What do extra block variables in Ruby built-in iterators do? - ruby

Say if you were using the built-in iterator .each method on a range:
(0..3).each do |x,y,z,a,b,c|
end
I know that x is the actual item in the collection, but what does defining additional block variables (y,z,a,b,c) to the block actually do? Do they represent something?

If the method does not give values, then the additional variables will be nil.
When running :
(0..3).each do |x,y|
puts x.class.to_s() + ' ' + y.class.to_s()
end
It outputs :
Integer NilClass
Integer NilClass
Integer NilClass
Integer NilClass
It all depends on what the method sends :
def myBlockCaller
yield 1, 2, 3
yield 'a', '5'
yield true
yield nil, 1
end
myBlockCaller do |x, y, z|
puts x.class.to_s() + ', ' + y.class.to_s() + ', ' + z.class.to_s()
end
Shows :
Integer, Integer, Integer
String, String, NilClass
TrueClass, NilClass, NilClass
NilClass, Integer, NilClass

They don't "do" anything. If you try it you will see they are nil.
(0..3).each do |x,y,z,a,b,c|
puts x.inspect
puts y.inspect
puts z.inspect
puts a.inspect
puts b.inspect
puts c.inspect
end
1
nil
nil
nil
nil
nil
2
nil
nil
nil
nil
nil
3
nil
nil
nil
nil
nil
Some iterators use more than one variable like each_with_index:
(1..3).each_with_index do |n,i|
n will hold the range item and i is it's index, i.e. 0,1,2
Or in a hash like:
{a: 1, b: 2, c: 3}.each do |k,v|
puts k
puts v
end
a:
1
b:
2
c:
3

Related

Give the code below, I was told that if a hash is called and pass through as argument, I can't return Nil. Can anyone explain to me how come?

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

#dig wrapper method to accept a dynamic number of variables

I'm trying to write a method that accepts an unknown number of arguments and performs a Hash#dig on them.
def unknown_dig(hash, *args)
# do some magic?
hash.dig(non_array_args)
end
#example usage
unknown_dig(hash, 'a', 'b', 'c')
Is this possible?
Hash#dig was bestowed upon us in Ruby v2.3. To support earlier Ruby versions you can use Enumerable#reduce (aka inject). This is how we did it when I was a kid.
def dig_it(h,*keys)
keys.reduce(h) { |obj,k| obj && obj[k] }
end
h = { a: { b: 1 } }
dig_it(h, :a, :b)
#=> 1
dig_it(h, :a)
#=> {:b=>1}
dig_it(h, :a, :c)
#=> nil
dig_it(h, :c, :b)
#=> nil
If obj is a hash, as it is initially (h), when k is passed to the block obj[k] #=> nil if obj does not have a key k (or if obj has a key k whose value if nil), in which case obj && obj[k] #=> obj && nil #=> nil. The block calculation will therefore be obj && obj[k] #=> nil && obj[k] #=> nil for each of the remaining elements of keys that are passed to the block. (nil[k] would raise an exception but it is never executed.) If the hash obj has a key k with value false, the same outcome will result except false (rather than nil) will be returned.
Hash#dig (defined in Ruby 2.3) already does this:
hash = { a: { b: 1 } }
hash.dig(:a, :b) == hash.dig(*[:a, :b])
If you want to it more functional style (where you pass the hash as an argument instead of calling the method on it), it's easy:
def hash_dig(hash, *args)
hash.dig(*args)
end

How to convert String to Number only when String including just number element

In ruby nil.to_f and "".to_f return 0.0, and "1foo2".to_f returns 1.0. In these cases I want to get nil.
What I want are:
nil.my_to_f #=> nil
"".my_to_f #=> nil
"1foo2".my_to_f #=> nil
"3".my_to_f #=> 3.0
"3.5".my_to_f #=> 3.5
'.3'.my_to_f #=> nil
'.'.my_to_f #=> nil
To enable this function I wrote like this:
match(/^([\d\.]+)$/){ $1.to_f if $1 }
It works fine for first five examples, but fails last two.
How can I convert String to Float like the examples?
If there is a Gem for it, I'm willing to use it.
Update
With my code nil raise error, sorry. I'll fix it.
You may use Float class for this. Beware it raises ArgumentError in case of wrong value:
def my_to_f(n)
Float(n)
rescue
0.0 if n == "."
end
my_to_f nil #=> nil
my_to_f "" #=> nil
my_to_f "1foo2" #=> nil
my_to_f "3" #=> 3.0
my_to_f "3.5" #=> 3.5
my_to_f ".3" #=> 0.3
my_to_f "." #=> 0.0
You could do this:
class NilClass
def my_to_f
nil
end
end
class String
def my_to_f
r = /
^ # match beginning of string
-? # optionally match minus sign
\d+ # match > 0 digits
(?:\.\d*)? # optionally match decimal, >= 0 digits in non-capture group
$ # match end of string
/x
s = self[r]
s ? s.to_f : nil
end
end
nil.my_to_f #=> nil
"".my_to_f #=> nil
"1foo2".my_to_f #=> nil
"3".my_to_f #=> 3.0
"3.5".my_to_f #=> 3.5
".3".my_to_f #=> nil
".".my_to_f #=> nil
"3.".my_to_f #=> 3.0
"-2.4". my_to_f #=>-2.4
If you want "3.".my_to_f to return nil, change \d* to \d+.

Not able to do Class(argument) with eval in ruby

I have a function like this:
def check_if_correct_type(type, value)
# nil.test!
# eval(type.classify(value)) rescue return false
# true
case type
when "integer"
!!Integer(value) rescue return false
when "float"
!!Float(value) rescue return false
else
return true
end
true
end
A sample would be
check_if_correct_type("integer", "a")
I tried changing the function like this:
check_if_correct_type(type, value)
!!(eval(type.classify(value))) rescue return false
true
end
This is throwing errors. How do I fix this. I am fairly new to meta programming so kind of lost.
Update 1:
"adfadf".kind_of?(String) #=> true
123.kind_of?(String) #=> false
# The "Fixnum" class is actually used for integers
"adfadf".kind_of?(Fixnum) #=> false
123123.kind_of?(Fixnum) #=> true
12.3.kind_of?(Float) #=> true
"sadf".kind_of?(Float) #=> false
12.kind_of?(Float) #=> false
The above will not work for me as the kind_of? function will find the type of the object where as for me the answer requires to be like this:
check_if_correct_type("integer", "1221") #=> true
check_if_correct_type("float", "1.24") #=> true
check_if_correct_type("string", "asds12") #=> true
check_if_correct_type("float", "asdasd1.24") #=> false
where as
"1.24".kind_of?(Float) #=> false
That is why conversion works for me. Hope the question is more clear now.
Update 2:
This is what I get if I use public send.
!!public_send("integer".capitalize("1"))
ArgumentError: wrong number of arguments (1 for 0)
from (pry):4:in capitalize'
[5] pry(main)> !!public_send("integer".classify("1"))
ArgumentError: wrong number of arguments (1 for 0)
from /home/aravind/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/activesupport-4.2.0/lib/active_support/core_ext/string/inflections.rb:187:inclassify'
Note: classify is a part of Ruby on Rails and not Ruby.
I suggest you write your method as follows:
def correct_type?(type, str)
case type.downcase
when "integer"
!!to_integer(str)
when "float"
!!to_float(str)
else
raise ArgumentError, "type must be 'integer' or 'float'"
end
end
where to_integer(value) (to_float(value)) is a method that returns value.to_i (value.to_f) if value is the string representation of an integer (a float), else returns nil. The methods to_integer and to_float are useful because they tell you both whether the string can be converted to the given numerical type, and if it can, give you the numerical value.
Before considering how you might implement to_integer and to_float, I would like to call into question the need for correct_type?. Rather than:
str = "33"
if correct_type?("integer", str)
n = str.to_i
puts n
else
...
end
would it not be better to write:
if (n = to_integer("33"))
puts n
else
...
end
There are basically two ways to write the methods to_integer and to_float. The first is the approach you took:
def to_integer(str)
raise ArgumentError unless str.is_a? String
s = str.gsub(/\s/,'')
Integer(s) rescue nil
end
def to_float(str)
raise ArgumentError unless str.is_a? String
s = str.gsub(/\s/,'')
return nil if to_integer(s)
Float(s) rescue nil
end
to_integer("3") #=> 3
to_integer("-3") #=> -3
to_integer("+ 3") #=> 3
to_integer("cat") #=> nil
to_integer("3.14") #=> nil
to_integer(:cat) #=> ArgumentError: ArgumentError
to_float("3.14") #=> 3.14
to_float("-3.14") #=> -3.14
to_float("+ 3.14") #=> 3.14
to_float("cat") #=> nil
to_float("3") #=> nil
to_float(:cat) #=> ArgumentError: ArgumentError
The second approach is to use a regular expression:
def to_integer(str)
raise ArgumentError unless str.is_a? String
s = str.gsub(/\s/,'')
s[/^[+-]?\s*\d+$/] ? s.to_i : nil
end
def to_float(str)
raise ArgumentError unless str.is_a? String
s = str.gsub(/\s/,'')
return nil if to_integer(s)
s[/^[+-]?\s*\d+\.\d+$/] ? s.to_f : nil
end
to_integer("3") #=> 3
to_integer("-3") #=> -3
to_integer("+ 3") #=> 3
to_integer("cat") #=> nil
to_integer("3.14") #=> nil
to_integer(:cat) #=> ArgumentError: ArgumentError
to_float("3.14") #=> 3.14
to_float("-3.14") #=> -3.14
to_float("+ 3.14") #=> 3.14
to_float("cat") #=> nil
to_float("3") #=> nil
to_float(:cat) #=> ArgumentError: ArgumentError
There is no need to use eval to send a message. You can just use send instead:
def check_if_correct_type(type, value)
!!send(type.capitalize, value) rescue return false
true
end
Note: there is no method named classify anywhere in either the Ruby core library or the Ruby standard libraries. Note also that it is a very bad idea, to just blindly catch all exceptions.
I don't see a point of using metaprogramming for this example. You should avoid using it where there's no need for it. Generally speaking, your program's logic should be:
a) Check the type of the value entered.
b) Compare the type with the type entered as argument. Or in code:
def check_if_correct_type(type, value)
actual_type = value.class.name
return actual_type.downcase == type.downcase
end
p check_if_correct_type('string', 'test') #=> true
p check_if_correct_type('integer', 'test2') #=> false
This could could be made even shorter in one line, but did it in two to demonstrate more clearly what's going on.
If you want to check an object's class, the right way is this:
"adfadf".kind_of?(String) #=> true
123.kind_of?(String) #=> false
# The "Fixnum" class is actually used for integers
"adfadf".kind_of?(Fixnum) #=> false
123123.kind_of?(Fixnum) #=> true
12.3.kind_of?(Float) #=> true
"sadf".kind_of?(Float) #=> false
12.kind_of?(Float) #=> false
There is no reason to be using the Integer() or Float() methods to check for a type. Those are type conversion methods, they will convert other types to Float or Fixnum. If you do want to try to convert a type is convertable to Float or numeric, that is one way to do it, but there might be better ways.
In general, you should never plan on raising and rescuing an exception as part of ordinary program flow; one reason is because it is very slow. Exceptions should be used for errors and unusual/exceptional conditions, not routine conditions such that exceptions will be frequently raised.
And definitely don't start bringing eval into it, geez why would you do that?
This is how I have ended up solving my problem
def check_if_correct_type(type, value)
!!eval("#{type.classify}(value)") rescue return false
true
end
Sample output for this function is below incase you are wondering if it words or not
[25] pry(main)> value = "1"
=> "1"
[26] pry(main)> !!eval("#{type.classify}(value)")
=> true
[27] pry(main)> value = "a"
=> "a"
[28] pry(main)> !!eval("#{type.classify}(value)")
ArgumentError: invalid value for Float(): "a"
from (pry):28:in `eval'
[29] pry(main)> value = "1.4"
=> "1.4"
[30] pry(main)> type = "integer"
=> "integer"
[31] pry(main)> !!eval("#{type.classify}(value)")
ArgumentError: invalid value for Integer(): "1.4"
from (pry):31:in `eval'

Function to strip!, then convert zero-length string to nil

What's the most-efficient manner to remove beginning and ending spaces around a string, then convert the string to nil if the resulting value is zero-length?
For example:
> a=''
> squash(a)
=> nil
> a=' '
> squash(a)
=> nil
> a=' xyz '
> squash(a)
=> 'xyz'
> a=nil
> squash(a)
=> nil
Thus far:
def squash(value)
return nil if value.nil?
value.strip!
(value.blank? ? nil : value)
end
Seems like there could be a more-terse way of implementing this.
** edit **
While I am working in Rails, it would be nice if the answer would contain a Ruby-only implementation, too.
I should emphasize that the implementation needs to be able to handle a string with a nil value.
Assuming you want this for rails (otherwise blank? is undefined) you can use presence method:
def squash(value)
value && value.strip.presence
end
In pure ruby, I would do:
def squash(value)
return unless value
value = value.strip
value unless value.empty?
end
This will work with plain Ruby:
def squash(str)
str = str.to_s.strip
str unless str.empty?
end
Here's one way:
def squash(str)
(str && str[/\S/]) ? str.strip : nil
end
/\S/ looks for a character that is not whitespace.
squash " My dog has fleas. " #=> "My dog has fleas."
squash " " #=> nil
squash nil #=> nil
Reader challenge
I tried to also implement squash!, that would convert the argument str in place. If str is nil, just leave it alone. If str contains a least one non-whitespace character, then str.strip!. However, I could not figure out a way to convert a string to nil. I wanted to do this when the string is empty or contains only whitespace, but the problem is to convert any string, or more generally, any non-nil object, to nil, when the object is received as a method argument. Can it be done? [Edit: #Stefan says the type cannot be changed. I'm sure he's right, but I would like to see where that is written and understand why it is not permitted. Anyone? tidE].
This handles all your examples.
def squash(value)
value.to_s.strip.empty? ? nil : value.strip
end
Just adding this because it's short:
def squash(str)
str.to_s[/\S(.*\S)?/]
end
squash(nil) #=> nil
squash("") #=> nil
squash(" ") #=> nil
squash("a") #=> "a"
squash(" a") #=> "a"
squash("a ") #=> "a"
squash(" a ") #=> "a"
squash(" foo ") #=> "foo"
squash(" foo bar ") #=> "foo bar"
Here's a plain ruby version:
def squash(str)
str && str.strip! && (str unless str.empty?)
end
Update - If you want a version without side effects:
def squash(str)
str && (x = str.strip) && (x unless x.empty?)
end
If you want the method name squash with argument value.
def squash(value)
return value unless value.instance_of?(String)
return if value.strip!&.empty?
value
end
Features:
Works either on pure Ruby or Ruby on Rails
Works with other data types than string as well, for example, you can pass a number if you want
Testing:
squash('ok')
#=> ok
squash('')
#=> nil
squash(' ')
#=> nil
squash(' xyz ')
#=> 'xyz'
squash('xyz ')
#=> 'xyz'
squash(' xyz')
#=> 'xyz'
squash(123)
#=> 123
squash(nil)
#=> nil
Note:
I use safe navigation operator, which was released in Ruby 2.3.0. So make sure before using it.
irb(main):001:0> s = " string "
=> " string "
irb(main):002:0> s.strip!
=> "string"
irb(main):003:0> s.blank?
NoMethodError: undefined method `blank?' for "string":String
from (irb):3
from C:/RUBY/BIN/irb:12:in `'
irb(main):004:0>
I think blank is not Ruby but Rails? Anyway, what's wrong with
(value.length == 0 ? nil : value)
or even better
value.empty? ? nil : value
At least everybody would understand what the intention is here.

Resources