inquisitive
inquisitive

Reputation: 3974

how to match the contents of two arrays and get corresponding index ruby

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

Answers (3)

Cary Swoveland
Cary Swoveland

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

Alex Pan
Alex Pan

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

pangpang
pangpang

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

Related Questions