Reputation: 195
Here is a ruby array:
array = ['x', 3, 0, 4, 4, 7]
I want to map through array
, take every integer in the array (except for 0
) minus 2, and return a new array.
When there are letters, no change will be made to the letters.
Example output:
['x', 1, 0, 2, 2, 5]
This is what I have, but I got the error message saying "undefined method integer? "can someone tell me what is the problem?
def minusNumber(array)
array.map do |e|
if e.integer? && e !== 0
e - 2
end
end
end
Upvotes: 0
Views: 538
Reputation: 2496
Short and sweet.
array.map { |v| v.is_a?(Integer) && !v.zero? ? v - 2 : v }
This eliminates all the needless case
statements, multiple iteration, and transformations. Simply takes each element, if it is an integer and greater than 0, then subtracts 2, otherwise does nothing.
Upvotes: 0
Reputation: 159
I think this is legible and elegant:
array.map { |v| v.is_a?(Integer) && v == 0 ? v : v -2 }
Upvotes: -1
Reputation: 3618
You can do this without checking for type.
use of kind_of? is a code smell that says your code is procedural, not object oriented… https://www.sandimetz.com/blog/2009/06/12/ruby-case-statements-and-kind-of
def someMethod(arr)
arr.map do |item|
next item unless item.to_i.nonzero?
item - 2
end
end
someMethod(["x", 3, 0, 4, 4, 7])
> ["x", 1, 0, 2, 2, 5]
Upvotes: 0
Reputation: 961
The other answers here will work fine with your current input. Something like:
def minusNumber(array)
array.map do |e|
if e.is_a?(Integer) && e != 0
e - 2
else
e
end
end
end
But here is a more flexible solution. This might be a bit too advanced for you where you are now, but all learning is good learning :-)
Ruby is a language that allows polymorphism in its variables. You can see, using the example input for your method, that the variable e
may contain a String
object or an Integer
object. But it actually may contain any type of object, and Ruby would not care one bit, unless it encounters an error in using the variable.
So. In your example, you need to keep Integer
s in the output. But what if in the future you need to pass in an array containing some Score
objects, and you'd need those in your output too? This is a brand new class that you haven't even written yet, but you know you will later on down the line. There's a way you can re-write your method that will anticipate this future class, and all other Integer
-type classes you may someday write.
Instead of using #is_a?
to check the type of the object, use #respond_to?
to check what methods it implements.
Any class that can be used as an integer should implement the #to_int
method. Integer
certainly does, and your future Score
class will, but String
does not. And neither does any other class that can't be considered like an integer. So this will work for all types of values, correctly separating those that respond to #to_int
and those that don't.
def minusNumber(array)
array.map do |e|
if e.respond_to?(:to_int) && e != 0
e - 2
else
e
end
end
end
Again, this stuff might be a bit advanced, but it's good to get in the habit early of thinking of variables in terms of its methods as opposed to its type. This way of thinking will really help you out later on.
Upvotes: 2
Reputation: 230296
Here's another take on this:
array = ['x', 3, 0, 4, 4, 7]
transformed = array.map do |e|
case e
when 0, String
e
when Integer
e - 2
else
fail 'unexpected input'
end
end
transformed # => ["x", 1, 0, 2, 2, 5]
It's a pity that you have to keep the elements from which you didn't subtract 2. I really wanted to do something like this
array.grep(Integer).reject(&:zero?).map{|i| i - 2 } # => [1, 2, 2, 5]
Couldn't find a way yet (which preserves the unprocessed items).
Upvotes: 2
Reputation: 2398
As mentioned in the comment above you should use is_a?
method to check the datatype of an element. The code below should work as expected:
def minusNumber(array)
array.map do |e|
if e.is_a?(String) || e == 0
e
else
e - 2
end
end
end
Instead of the if statement you could also do:
(e.is_a?(String) || e == 0 ) ? e : e-2
Please note that the original object is unchanged unless you use map!
Upvotes: -1