Ruby array.each with non unique integer values - arrays

We have array = [4, 2, 9, 11, 2, 16]
Then we have
indexes = []
for i in array do
if i > 0 then indexes << array.find_index(i) else next end
end
When printing out the result it returns
[0, 1, 2, 3, 1, 5]
The problem is with the fourth index. It should be 4, but it's 1 and that's because index 1 and 4 of array have the same value (which is 2).
Isn't for loop (or .each) supposed to go through all the elements one by one? Why is this happening? Why is it picking up second index of array twice?

array.find_index returns the first index of an element in array matching the passed value.
If you want the index of the value you're looking for then you should be iterating with each_with_index:
indexes = []
array.each_with_index do |value, index|
indexes << index if value > 0
end
Or more compact (with just a single array allocation):
indexes = array.each_with_object([]).with_index {|(v, o), i| o << v if i > 0 }
Or allowing for multiple allocations:
indexes = array.map.with_index {|v, i| v > 0 ? i : nil }.compact
Or:
indexes.map.with_index.select {|v| v.first > 0 }.map(&:last)

Because Array#find_index returns the index of the first element it finds in the array.
Returns the index of the first object in ary such that the object is == to obj.

Related

All_else method (Ruby)

I'm trying to create a method that iterates through an array and add up all of its elements and returns the element that is half of its sum, else it will return nil.
Examples:
p all_else_equal([2, 4, 3, 10, 1]) #=> 10, because the sum of all elements is 20
p all_else_equal([6, 3, 5, -9, 1]) #=> 3, because the sum of all elements is 6
p all_else_equal([1, 2, 3, 4]) #=> nil, because the sum of all elements is 10 and there is no 5 in the array.
My solution was to iterate through the array and add each element together using a 'sum' variable. Then write a conditional statement stating if half of the sum is included in the arr, then return the element, else return nil. But for what ever reseason I keep getting 'nil'. Can anyone out there tell me why this is wrong? Here's my code:
def all_else_equal(arr)
sum = 0
sum_half = sum / 2
arr.each_with_index do |ele, i|
sum += ele
if sum_half == ele
return ele
else
return nil
end
end
end
console:
nil
so your code will return nil right after the first value. this is because
the return condition is in the loop. to solve this, move it out as shown below.
also, create the sum_half variable after the sum has already been
evaluated:
def all_else_equal(arr)
sum = 0
arr.each_with_index do |ele, i|
sum += ele
end
sum_half = sum / 2
if arr.include?(sum_half) #check if sum_half in array
return sum_half
else
return nil
end
end
p all_else_equal([2, 4, 3, 10, 1]) #=> 10, because the sum of all elements is 20
p all_else_equal([6, 3, 5, -9, 1]) #=> 3, because the sum of all elements is 6
p all_else_equal([1, 2, 3, 4]) #=> nil, because the sum of all elements is 10 and there is no 5 in the array.
a simpler alternative:
def all_else_equal(arr)
sum_half = arr.sum / 2
arr.include?(sum_half) ? sum_half : nil
end
p all_else_equal([2, 4, 3, 10, 1]) #=> 10, because the sum of all elements is 20
p all_else_equal([6, 3, 5, -9, 1]) #=> 3, because the sum of all elements is 6
p all_else_equal([1, 2, 3, 4]) #=> nil, because the sum of all elements is 10 and there is no 5 in the array.
I see a few things here. First, in Ruby and all imperative languages, variable assignments are evaluated only at their time of execution -- so your sum_half variable will always be equal to 0 / 2 or 0. It will not dynamically re-evaluate to always be equal to sum / 2. You would need to recompute it after every iteration of the loop for it to be accurate.
Second, from a logical perspective, your sum variable is only really the sum so far. Checking if half of it is equal to the current element is not what you want to do, because even if that's true, it doesn't mean the current element is half of the final sum. Instead, you might want to find the full sum, divide it in two, and then look for an element that matches that value.
Also, stylistically, your each_with_index is currently unnecessary because you're not using the index at all -- change it to just an each until you find a use for that index value.
#RobertNubel and #PhiAgent have great answers - I would suggest you especially work through PhiAgent's Answer.
I will only add a worked example for the first iteration of the loop so you can see exactly what is happeneing
A worked Example with Comments:
def all_else_equal(arr) ## let's say array = [1,2] is passed in as a parameter
sum = 0
sum_half = sum / 2 ## => sum_half = 0
arr.each_with_index do |ele, i| # => elem = 1 (given the first element in the array)
sum += ele # => sum is now 1
if sum_half == ele # => 0 == 1 ## this will be false
return ele
else
return nil # => nil be returned
end
end
end
#PhiAgent has a great solution to get the code working.

Ruby: How to compare elements one by one within an array?

Here is first question on stackoverflow :)
consider a random array composed of any integers
a = [5, 10, 2, 3, 56]
I would like to write a code that will compare each element likewise: element of index i (el_i) with element of index i + 1 (el_i+1) (and continue comparison with element index i + 1 (el_i+1) with element of index i + 2 (el_i+2) till a.length). Then I would like to sum elements according to the result of the comparison. Here is an example of a failed attempt to picture it:
def conditional_sum(array)
sum = 0
array.each_with_index do |element, i|
if array[i] >= array[i + 1]
sum += element
else
sum -= element
end
end
sum
end
Another consisted in making 2 arrays from the previous one and delete the last element (I spare you the code to obtain them) an compare elements that have the same index likewise:
a = [5, 10, 2, 3, 56]
a_bis = [5, 10, 2, 3]
b = [10, 2, 3, 56]
sum = 0
for i in [0..a_bis.length]
if a_bis[i] >= b[i]
sum += a_bis[i] + b[i]
else
sum -= a_bis[i] + b[i]
end
end
p sum
doesn't work either... Many thanks for your help!
If I understood your question:
a = [5, 10, 2, 3, 56]
a.each_cons(2).sum do |first, second|
first > second ? first - second : first + second
end
#=>87
#each_cons will group them into pairs of consecutive elements:
a.each_cons(2) do |pair|
p pair
end
#=>[5, 10]
# [10, 2]
# [2, 3]
# [3, 56]
Then you can execute the desired calculation for each pair inside the block.
your first example was almost working. Trying it raise the error:
`>=': comparison of Integer with nil failed (ArgumentError)
It come from the fact that for the last iteration of the index, you compare the last value 56 with nil. Ie 56 >= nil has no sense for ruby, therefore the error. To fix it, you can iterate without the last element. As always in ruby, their are multiple ways to do it. Here is mine :
def conditional_sum(array)
sum = 0
# array[0...-1] will iterate without le last element
array[0...-1].each_with_index do |element, i|
if array[i] >= array[i + 1]
sum += element
else
sum -= element
end
end
sum
end
Another solution to do what you want would be the following (more functional style). Using the #each_cons to select each pair
def conditional_sum(array)
array.each_cons(2)
.sum {|i,j| i >= j ? i : -i}
end

Compare an element from one array with an element from another array

I have two arrays of numbers that have the same size. How can I tell if there is any element in the second array that is greater than the first array at a given index? With this example:
a = [2, 8, 10]
b = [3, 7, 5]
3 is greater than 2 at position 0. But in the following:
a = [1, 10]
b = [0, 8]
there is no such element. At index 0, 0 is not greater than 1, and at index 1, 8 is not greater than 10.
Try this one
a.each_with_index.any? { |item, index| b[index] > item }
No need for indices. Just pair them and check each pair.
b.zip(a).any? { |x, y| x > y }
=> true or false
And a tricky one: Check whether at every position, a is the maximum:
a.zip(b).map(&:max) != a
=> true or false
And a very efficient one (both time and space):
b.zip(a) { |x, y| break true if x > y }
=> true or nil
(If you need true/false (often you don't, for example in if-conditions), you could prepend !! or append || false)
If there's a number in b greater than the number in a at the given index, this will return the number in b. If no numbers in b are greater, nil will be returned.
b.detect.with_index { |n, index| n > a[index] }
For example, if you have the following arrays.
a = [3, 4, 5]
b = [6, 7, 8]
You'll get a return value of 6.

Unable to pass array to method in Ruby

So I have this function.
array = Array.new
def put_array(array)
l = array.index(array[-1])
puts l
for i in 0...l do
puts array[i]
end
end
put_array([1, 2, 3, 4, 5, 6])
the output is to print the array but I keep getting this result.......
5
1
2
3
4
5
Error ::The index output is 5, whereas the expected output should be 6. I have tried rindex,array.last instead of array[-1] but I keep getting same answer
I'm not exactly sure what your expected output was.
But your error comes from the fact that array indexing in ruby is zero-based, ie they start at 0. Therefore :
Despite your array having 6 elements, the index of its last element is 5.
This also mean that when you start iterating through your array with an index value of 1, you are starting with its second element.
On a different note, with 1...l you are defining an exclusive range, that is a range of integers from 1 to l, excluding l. If you want to include l, you need to use 1..l
A quickfix to your code would be :
for i in 0..l do
puts array[i]
end
But a more idiomatic way of achieving the same output would be :
array.each {|e| puts e}
Also
l = array.index(array[-1])
looks a bit convoluted. Why not simply ?
l = array.length
If you simply puts array it will print each element of array in new line:
array = [1, 2, 3, 4, 5, 6]
puts array
#=> Output:
1
2
3
4
5
6
def put_array(array)
puts array
end
> put_array(array)
If you want to perform on index basis:
> array.each_with_index {|e,i| puts array[i]}
I think you are confused because 0...5 outputs the number range 0,1,2,3,4 and not 0,1,2,3,4,5. You need to use .. rather than ... at the line starting for i in
def put_array(array)
l = array.index(array[-1])
puts l
for i in 0..l do
puts array[i]
end
end
put_array([1, 2, 3, 4, 5, 6])
That will output:
5
1
2
3
4
5
6
Though as others have stated, there are easier ways to achieve the same output.
I am not sure if you are trying to print the array, if it so .. your code can be modified this way
array = Array.new
def put_array(array)
l = array.length
for i in 0...l do
puts array[i]
end
end
put_array([1, 2, 3, 4, 5, 6])
index gives you the index for the given object, so in your case index(-1) will give nil because the array does not contain -1.
Instead you want to do this:
array.index(6) # => 5 (6 is at index 5 of array)
array[5] # => 6 (5th item in array is 6)
array[-1] # => 6 (-1th (aka last) item in the array is 6)
array.last # => 6 (last item in array is 6)

Changing an element in a multidimensional array relative to the location of another element

Say I have a multidimensional array...
#arr = [[0,0,0,0], [0,0,1,0], [0,0,0,0], [0,0,0,0]]
and I want to change the 0 four indexes from the left of the 1 to a 1, so i.e. the first subarray to become [0,0,1,0]... why doesn't this work...
#arr.map! { |x| ( x[1][-2] == 1 ? x[0][-2] = 1 : x ) }
In your block, x now represents each individual sub-array.
x = [0, 0, 1, 0]
#=> x[1][-2] = 0

Resources