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'
Related
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
"0".to_i == 0
also:
"abcdefg".to_i == 0
I want to make sure the string I'm parsing really is just a number (0 included).
Integer("0").zero? rescue false
# => true
Integer("1").zero? rescue false
# => false
Integer("abcdefg").zero? rescue false
# => false
def string_is_integer?(string)
!string.match(/^(\d)+$/).nil? # \d looks for digits
end
def string_is_float?(string)
!string.match(/^(\d)+\.(\d)+$/).nil?
end
def string_is_number?(string)
string_is_integer?(string) || string_is_float?(string)
end
Or, if you don't mind:
def string_is_number?(string)
begin
true if Float(string)
rescue ArgumentError
end || false
end
def is_zero?(string)
(Integer(string) rescue 1).zero?
end
is_zero? "0" #=> true
is_zero? "00000" #=> true
is_zero? "0cat" #=> false
is_zero? "1" #=> false
is_zero? "-0" #=> true
is_zero? "0_000" #=> true
is_zero? "0x00" #=> true
is_zero? "0b00000000" #=> true
Several of these examples illustrate why it's preferable to use Kernel#Integer rather than a regular expression.
First test if the string is integer or not and then match
def is_i?(x)
!!(x =~ /\A[-+]?[0-9]+\z/)
end
def is_zero?(x)
return is_i?(x) && x.to_i == 0
end
# is_zero?("0") will return true
# is_zero?("abcde") will return false
Or you can put these methods in String class like this
class String
def is_i?
!!(self =~ /\A[-+]?[0-9]+\z/)
end
def is_zero?
self.is_i? && self.to_i == 0
end
end
"0".is_zero? # true
"abcde".is_zero? # false
I want to make sure the string I'm parsing really is just a number.
There are two steps involved:
Parsing, i.e. converting the string into a number
Validation, i.e. checking if the string actually is a number
The built-in conversion functions like Integer and Float already perform both steps: they return a numeric result if the string is valid and raise an error otherwise.
Using these functions just for validation and discarding the return value is wasteful. Instead, use the return value and handle the exception, e.g.:
begin
puts 'Enter a number:'
number = Float(gets)
rescue
puts 'Not a valid number'
retry
end
# do something with number
or something like:
def parse_number(string)
Float(string) rescue nil
end
number = parse_number(some_string)
if number
# do something with number
else
# not a number
end
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.
How to check if a variable is a number or a string in Ruby?
There are several ways:
>> 1.class #=> Fixnum
>> "foo".class #=> String
>> 1.is_a? Numeric #=> true
>> "foo".is_a? String #=> true
class Object
def is_number?
to_f.to_s == to_s || to_i.to_s == to_s
end
end
> 15.is_number?
=> true
> 15.0.is_number?
=> true
> '15'.is_number?
=> true
> '15.0'.is_number?
=> true
> 'String'.is_number?
=> false
var.is_a? String
var.is_a? Numeric
The finishing_moves gem includes a String#numeric? method to accomplish this very task. The approach is the same as installero's answer, just packaged up.
"1.2".numeric?
#=> true
"1.2e34".numeric?
#=> true
"1.2.3".numeric?
#=> false
"a".numeric?
#=> false
class Object
def numeric?
Float(self) != nil rescue false
end
end
Print its class, it will show you which type of variable is (e.g. String or Number).
e.g.:
puts varName.class
if chr.to_i != 0
puts "It is number, yep"
end
Why isnt that working:
>> s = "hi"
=> "hi"
>> s == ("hi"|"ho")
NoMethodError: undefined method `|' for "hi":String
from (irb):2
>>
I don't get it.. Is there a solution for this kind of syntax? Because
s == ("hi"|"ho")
#is shorther than
s == "hi" || s == "ho"
Yes, the bitwise operator | is not defined in the String class: http://ruby-doc.org/core/classes/String.html
Consider this for expressiveness:
["hi", "ho"].include? myStr
irb(main):001:0> s = "hi"
=> "hi"
irb(main):002:0> ["hi", "ho"]
=> ["hi", "ho"]
irb(main):003:0> ["hi", "ho"].include? s
=> true
irb(main):004:0> s = "foo"
=> "foo"
irb(main):005:0> ["hi", "ho"].include? s
=> false
In most high level languages that syntax will not work, you have to stick to the longer syntax of:
s == "hi" || s == "ho"
Note that | is a bitwise or, whereas || is a regular or
You could use the include? method on array if you've got several == tests to do:
["hi", "ho"].include?(s)
Not shorter for two checks admittedly but it will be shorter for three or more.
This syntax doesn't exist in any language as far as I know.
What you are saying
s == ("hi"|"ho")
Literally translates to 'bitwise OR the strings "hi" and "ho" together and then compare them with s'. If you can't see why this is not what you are looking for, try writing down the ASCII codes for "hi" and "ho" and then bitwise ORing them together. You are going to get complete gibberish.
You could make it work that way:
irb> class Pair
def initialize(strA,strB)
#strA,#strB = strA,strB
end
def ==(string)
string == #strA || string == #strB
end
def |(other)
Pair.new(self,other)
end
end
#=> nil
irb> class String
def |(other)
Pair.new(self,other)
end
alias old_equals :==
def ==(other)
if other.kind_of? Pair
other == self
else
old_equals other
end
end
end
#=> nil
irb> ("one"|"two") == "one"
#=> true
irb> ("one"|"two") == "two"
#=> true
irb> ("one"|"two") == "three"
#=> false
irb> "one" == ("one"|"two")
#=> true
irb> "three" == ("one"|"two"|"three")
#=> true
But since this involves some monkey-patching of a fairly lowlevel class, I wouldn't advise relying on it. Other people will hate reading your code.
Ruby supports binary 'or' and other binary operations on values of type Fixnum and Bignum, meaning any integer. Bitwise operations aren't supported on strings or any other type, as far as I know.
As other people have mentioned, you probably want something other than binary operations altogether. However, you can easily get integer representations of characters, so you can compare characters like so:
a = "Cake"
b = "Pie"
puts a[0] | b[0] # Prints "83" - C is 67 and P is 80.
You can get an array of the comparisons easily with some conversions.
a = "Cake"
b = "Pie " # Strings of uneven length is trivial but more cluttered.
a_arr = a.split(//)
b_arr = b.split(//)
c_arr = []
a.each_with_index { |char, i| c.push(a[i].to_i | b[i].to_i) }
# If you *really* want an ASCII string back...
c = c_arr.collect(&:chr).join
You could use a regex:
Like so:
regex = /hi|ho/
s = "hi"
t = "foo"
s =~ regex
#=> 0
t =~ regex
#=> nil