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
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 Integers 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.
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).
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]
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.
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!
I think this is legible and elegant:
array.map { |v| v.is_a?(Integer) && v == 0 ? v : v -2 }
Related
Lets say I have the following array:
array_sample = [2, 7, 3, -5, 2, -6]
Is there a method to replace '-5' and '-6' with 0 (or any other potential negative values)?
I have tried the following which does not seem to work:
for i in array_sample
if array_sample[i] < 0
array_sample[i] = 0
end
Any suggestions would be appreciated, because this seems very simple!
Here's another snippet:
array_sample.map!{|item| [0, item].max}
This replaces each array value with the original item, or 0, whichever is greater. If you want a new array that doesn't mutate the current array (good idea often), you can use map instead of map!.
Try this
array_sample = [2, 7, 3, -5, 2, -6]
array_sample.map! { |e| e > 0 ? e : 0 }
Response
[2, 7, 3, 0, 2, 0]
You can try using map
array_sample.map do |int|
if (int < 0)
0
else
int
end
end
To directly translate your example to correct code, you could do this:
array_sample.each_with_index do |value, index|
if value < 0
array_sample[index] = 0
end
end
But other answers may be more idiomatic.
Note that ruby doesn't iterate using numbers by default, like c-like languages often do. Most of the time ruby gives you back each item, not the index and then you have to look up the number. (It hides the index unless you ask for it)
Ruby has lots of nice ways of iterating and directly returning that result. This mostly involve array methods. For example:
def ten_times_tables
(1..5).map { |i| i * 10 }
end
ten_times_tables # => [10, 20, 30, 40, 50]
However, I sometimes want to iterate using while and directly return the resulting array. For example, the contents of the array may depend on the expected final value or some accumulator, or even on conditions outside of our control.
A (contrived) example might look like:
def fibonacci_up_to(max_number)
sequence = [1, 1]
while sequence.last < max_number
sequence << sequence[-2..-1].reduce(:+)
end
sequence
end
fibonacci_up_to(5) # => [1, 1, 2, 3, 5]
To me, this sort of approach feels quite "un-Ruby". The fact that I construct, name, and later return an array feels like an anti-pattern. So far, the best I can come up with is using tap, but it still feels quite icky (and quite nested):
def fibonacci_up_to(max_number)
[1, 1].tap do |sequence|
while sequence.last < max_number
sequence << sequence[-2..-1].reduce(:+)
end
end
end
Does anyone else have any cleverer solutions to this sort of problem?
Something you might want to look into for situations like this (though maybe your contrived example fits this a lot better than your actual use case) is creating an Enumerator, so your contrived example becomes:
From the docs for initialize:
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
and then call it:
p fib.take_while { |elem| elem <= 5 }
#=> [1, 1, 2, 3, 5]
So, you create an enumerator which iterates all your values and then once you have that, you can iterate through it and collect the values you want for your array in any of the usual Ruby-ish ways
Similar to Simple Lime's Enumerator solution, you can write a method that wraps itself in an Enumerator:
def fibonacci_up_to(max_number)
return enum_for(__callee__, max_number) unless block_given?
a = b = 1
while a <= max_number
yield a
a, b = b, a + b
end
end
fibonacci_up_to(5).to_a # => [1, 1, 2, 3, 5]
This achieves the same result as returning an Enumerator instance from a method, but it looks a bit nicer and you can use the yield keyword instead of a yielder block variable. It also lets you do neat things like:
fibonacci_up_to(5) do |i|
# ..
end
I need to compare each value in ruby array with previous and next value.
Update
Example:
[1,2,4,5]
I want to check like this. (a[i] with a[i-1] and a[i+1])
1 with only next value # as there is no prev value
2 with prev & next value
4 with prev & next value
5 with only prev value # as there is no next value
In ruby, the a[-1] is not pointing to nil, it is taking last value. So, unable to iterate. Is there any alternate solution?
Tried
changing array to [nil,1,2,4,5,nil]
but getting following error
comparison of Fixnum with nil failed (ArgumentError)
instead of 0..n I tried 1...n. but this does not solve my issue.
Question:
How to ignore negative indices for first(i-1) and last(i+1) element in ruby array.
Your comparison doesn't really make sense. You are comparing everything twice, but if someone really is changing the array while you are iterating over it, you have much bigger problems than this (and you still will not catch modifications made to the beginning of the array when you are already in the middle). It is enough to compare each consecutive pair of elements, which is easily done:
[1, 2, 4, 5].each_cons(2).all? {|a, b| a < b }
If you really absolutely MUST compare triples, that is also easily done:
[1, 2, 4, 5].each_cons(3).all? {|a, b, c| a < b && b < c }
And if you want to make the size of the sliding window generic, then you can do something like this:
[1, 2, 4, 5].each_cons(n).all? {|window|
window.each_cons(2).map {|a, b| a < b }.inject(:&)
}
I need to compare each value in ruby array with previous and next
value.
This method takes an array and a comparison method such as :<, :> or :== etc
def prev_next arr, com
arr.map.with_index { |e,i|
if i == 0
[ e,
e.send(com,arr[i.succ])
]
elsif i == arr.length-1
[ e.send(com,arr[i.pred]),
e
]
else
[ e.send(com,arr[i.pred]),
e,
e.send(com,arr[i.succ])
]
end
}
end
arr = [1,2,3,4,5]
p prev_next(arr,:<)
#=> [[1, true], [false, 2, true], [false, 3, true], [false, 4, true], [false, 5]]
Note the second parameter can be passed as a string or a symbol because send is clever enough to convert strings to symbols.
Notable methods: Object#send, Fixnum#succ and Integer#pred
Now I totally agree with Jörg here that each_cons is the way to go and you should probably look for some other structure of the data if comparing the data is this complicated.
With that said. Nothing prevents normal index lookups in Ruby, and if nothing else works just implement your requirements in a simple case statement:
my_array = [1,2,4,5]
my_array.size.times do |ix|
case ix
when 0 then my_array[ix] == my_array[ix+1]
when my_array.size-1 then my_array[ix] == my_array[ix-1]
else my_array[ix-1] == my_array[ix] == my_array[ix+1]
end
end
I was going over some interview questions and came across this one at a website. I have come up with a solution in Ruby, and I wish to know if it is efficient and an acceptable solution. I have been coding for a while now, but never concentrated on complexity of a solution before. Now I am trying to learn to minimize the time and space complexity.
Question:
You have an array of integers, and for each index you want to find the product of every integer except the integer at that index.
Example:
arr = [1,2,4,5]
result = [40, 20, 10, 8]
# result = [2*4*5, 1*4*5, 1*2*5, 1*2*4]
With that in mind, I came up with this solution.
Solution:
def find_products(input_array)
product_array = []
input_array.length.times do |iteration|
a = input_array.shift
product_array << input_array.inject(:*)
input_array << a
end
product_array
end
arr = find_products([1,7,98,4])
From what I understand, I am accessing the array as many times as its length, which is considered to be terrible in terms of efficiency and speed. I am still unsure on what is the complexity of my solution.
Any help in making it more efficient is appreciated and if you can also tell the complexity of my solution and how to calculate that, it will be even better.
Thanks
def product_of_others(arr)
case arr.count(0)
when 0
total = arr.reduce(1,:*)
arr.map { |n| total/n }
when 1
ndx_of_0 = arr.index(0)
arr.map.with_index do |n,i|
if i==ndx_of_0
arr[0,ndx_of_0].reduce(1,:*) * arr[ndx_of_0+1..-1].reduce(1,:*)
else
0
end
end
else
arr.map { 0 }
end
end
product_of_others [1,2,4,5] #=> [40, 20, 10, 8]
product_of_others [1,-2,0,5] #=> [0, 0, -10, 0]
product_of_others [0,-2,4,5] #=> [-40, 0, 0, 0]
product_of_others [1,-2,4,0] #=> [0, 0, 0, -8]
product_of_others [1,0,4,0] #=> [0, 0, 0, 0]
product_of_others [] #=> []
For the case where arr contains no zeroes I used arr.reduce(1,:*) rather than arr.reduce(:*) in case the array is empty. Similarly, if arr contains one zero, I used .reduce(1,:*) in case the zero was at the beginning or end of the array.
For inputs not containing zeros (for others, see below)
Easiest (and relatively efficient) to me seems to first get the total product:
total_product = array.inject(1){|product, number| product * number}
And then map each array element to the total_product divided by the element:
result = array.map {|number| total_product / number}
After initial calculation of total_product = 1*2*4*5 this will calculate
result = [40/1, 40/2, 40/4, 40/5]
As far as I remember this sums up to O(n) [creating total product: touch each number once] + O(n) [creating one result per number: touch each number once]. (correct me if i am wrong)
Update
As #hirolau and #CarySwoveland pointed out, there is a problem if you have (exactly 1) 0 in the input, thus:
For inputs containing zeros (workaroundish, but borrows performance benefit and complexity class)
zero_count = array.count{|number| number == 0}
if zero_count == 0
# as before
elsif zero_count == 1
# one zero in input, result will only have 1 non-zero
nonzero_array = array.reject{|n| n == 0}
total_product = nonzero_array.inject(1){|product, number| product * number}
result = array.map do |number|
(number == 0) ? total_product : 0
end
else
# more than one zero? All products will be zero!
result = array.map{|_| 0}
end
Sorry that this answer by now basically equals #CarySwoveland, but I think my code is more explicit.
Look at the comments about further performance considerations.
Here is how I would do it:
arr = [1,2,4,5]
result = arr.map do |x|
new_array = arr.dup # Create a copy of original array
new_array.delete_at(arr.index(x)) # Remove an instance of the current value
new_array.inject(:*) # Return the product.
end
p result # => [40, 20, 10, 8]
I not know ruby, but, accessing an array is O(1), that means that is in constant time, so the complexity of your algorithm is O(n), it is very good. I don't think that a better solution can be found in terms of complexity. The real speed is another issue, but that solution is fine
When I run this, it correctly changes the 3 in array to "blue", but it changes all other elements to nil.
array = [1, 2, 3, 4]
array.map! do |number|
if number == 3
array[number] = "blue"
end
end
Since I introduced the if statement, I expect it to leave everything else alone. Can someone explain why that's happening and how to get it to only modify the element isolated with the if statement?
When you run map! it:
Invokes the given block once for each element of self, replacing the
element with the value returned by the block.
So when the number doesn't match 3 the block is returning nil - when it is 3 you actually don't need the assignment you're using, having simply "blue" would have the same effect.
This should accomplish what you're trying to do:
array = [1, 2, 3, 4]
array.map! do |number|
number == 3 ? "blue" : number
end
See http://ruby-doc.org/core-2.2.0/Array.html#method-i-map-21