Reputation: 3974
Original Question: The original question was how to iterate to the next element in nested loop ruby. The answer taught an idiomatic approach to solve my problem and it needed a different question, so that when a user lands up here on googling, finds the correct thing
I have this requirement where there are two arrays, and each of them have unique values, in sorted order.
array1 = [2,3,4,5,6,7] #can not have repeated values[2,2,3]
array2 = [2,5,7]
I want to match elements of both arrays and print match found
whenever a match is found, along with the index of both the arrays. This is the code which works fine.
array1.each do |arr1|
array2.each do |arr2|
if (arr1==arr2)
puts ("Match found element #{arr1} #{array1.index(arr1)} #{array2.index(arr2)}")
#some code here to move to the next element in array 1 and array 2 and continue looping from there
end
end
end
But this does not use the data structure's quality of being unique and sorted in any way. For instance, when in the above example, element 2
in array1
matches element 2
in array2
, the element 2
in array2
should not continue trying to match other elements of array1
and also array1
should be moved to the next. I know there is something called next
. But I think that just returns the next element and does not move the iterator to the next element? Also I have to move to the next of both the arrays. How do I go about doing that?
Upvotes: 2
Views: 3615
Reputation: 110665
Here's another way:
array1 = [2,3,4,5,6,7]
array2 = [2,5,7]
h = array1.each_with_index.to_h
#=> {2=>0, 3=>1, 4=>2, 5=>3, 6=>4, 7=>5}
array2.each_with_index.with_object({}) { |(v,i),g| g[v] = [h[v],i] if h.key?(v) }
#=> {2=>[0, 0], 5=>[3, 1], 7=>[5, 2]}
The second expression can be broken down as follows:
e0 = array2.each_with_index
#=> #<Enumerator: [2, 5, 7]:each_with_index>
We can see the elements of this enumerator by converting it to an array:
e0.to_a
#=> [[2, 0], [5, 1], [7, 2]]
e1 = e0.with_object({})
#=> #<Enumerator: #<Enumerator: [2, 5,7]:each_with_index>:with_object({})>
e1.to_a
#=> [[[2, 0], {}], [[5, 1], {}], [[7, 2], {}]]
The elements of the enumerator e
are passed to the block and assigned to the block variables by Enumerator#each. The first (obtained using Enumerator#next) is:
(v,i),g = e1.next
#=> [[2, 0], {}]
v #=> 2
i #=> 0
g #=> {}
We can now execute the block:
g[v] = [h[v],i] if h.key?(v)
#=> g[2] = [h[2],0] if h.key(v)
# g[2] = [0,0] if true
The next element of e1
is now passed to the block:
(v,i),g = e1.next
#=> [[5, 1], {}]
v #=> 5
i #=> 1
g #=> {2=>[0, 0]}
g[v] = [h[v],i] if h.key?(v)
# g[5] = [h[5], 1] if h.key?(5)
# g[5] = [3, 1] if true
g #=> {2=>[0, 0], 5=>[3, 1]}
The calculation for the last element of e1
is similar.
Upvotes: 2
Reputation: 4571
If you want to find matching elements between two arrays just use &
like so:
array1 = [2,3,4,5,6,7] #does not have repeated values[2,2,3] and the values are sorted
array2 = [2,5,7]
matches = array1 & array2
=> [2, 5, 7]
To print the matches and indexes found in each array, just loop through the matches array like so:
matches.each do |number|
puts "Match found element #{number}"
puts "array1 index=#{array1.rindex(number)}"
puts "array2 index=#{array2.rindex(number)}"
end
Match found element 2
array1 index=0
array2 index=0
Match found element 5
array1 index=3
array2 index=1
Match found element 7
array1 index=5
array2 index=2
Upvotes: 7
Reputation: 8821
For your codes, you just need to use break
to exit the array2 loop, move to the next element in array1.
array2.each_with_index do |arr1, i1|
array1.each_with_index do |arr2, i2|
if (arr1==arr2)
puts ("Match found element array1: #{arr1} #{i1} and array2: #{arr2} #{i2}")
#if match element is found, exit from the current loop
break
end
end
end
Match found element array1: 2 0 and array2: 2 0
Match found element array1: 5 1 and array2: 5 3
Match found element array1: 7 2 and array2: 7 5
or
(array1 & array2).map{ |e| puts "Match found element #{e}" }
Match found element 2
Match found element 5
Match found element 7
Upvotes: 3