user14069773
user14069773

Reputation: 175

Ruby - Compare arrays and get index based on condition

I have two arrays

array_input = %w[one two three two]
array_compare = %w[one two three four five]

I want to extract the 'highest' index from the array_compare array, if the value exists in the input array. The desired output is 2 as three exists in the input array and the compare array.

I've tried

val = nil
array_compare.reverse_each do |v|
  val = v and break if array_input.include? v
end

but it does not set val.

Upvotes: 2

Views: 336

Answers (5)

TeWu
TeWu

Reputation: 6536

The most performant solution that I could come up with is:

def find_last_index(set_input, array_compare)
  (array_compare.length - 1).downto(0) do |i|
    return i if set_input.include?(array_compare[i])
  end
end

Note that the argument set_input is a Set, not an Array. Converting an array to a set makes sense, but only if you want to call find_last_index many times with the same set. Otherwise the process of converting an array to a set (to_set) takes more time, than you can gain using a Set#include? instead of an Array#include?. So if you want to use find_last_index only once, you shouldn't call find_last_index(array_input.to_set, array_compare), but instead use this version that don't use sets at all:

def find_last_index(array_input, array_compare)
  (array_compare.length - 1).downto(0) do |i|
    return i if array_input.include?(array_compare[i])
  end
end

You might want to see this benchmark of different solutions to this problem.

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110665

One could write

array_compare.rindex { |e| array_input.include?(e) }
  #=> 2

but that requires a linear search of array_input for each element of array_compare (starting with the last) until a match is found. Better is the following.

array_compare.rindex((array_compare & array_input).last)
  #=> 2

The steps are as follows.

a = array_compare & array_input
  #=> ["one", "two", "three"]

See Array#&. Note that "The order is preserved from the original array [array_compare].". This one-pass operation will be very fast as it is implemented in C. Continuing,

e = a.last
  #=> "three" 
array_compare.rindex(e)
  #=> 2

See Array#rindex.

Upvotes: 1

Timur Shtatland
Timur Shtatland

Reputation: 12347

Convert the array used for lookups to a set, for faster lookup.
Iterate from the end of the array where you are searching for the max index (as you are correctly doing), also for speed. Solutions that iterate from the beginning of the array and choose the max index are generally slower due to all the useless lookups until the last matching element is found. The method below stops quickly - at the first successful match.
Finally, correct the max index, since array_compare.reverse.each_with_index returns indexes of the reversed array.
The resulting code may be longer than in many of the other answers, but it is both simple and fast:

require 'set'
array_input = %w[one two three two]
array_compare = %w[one two three four five]
set_input = array_input.to_set
i_max = nil
array_compare.reverse.each_with_index { |x, i| i_max = i and break if set_input.include? x }

# correct the index to count from the beginning of 
# array_compare, not from the end:
i_max = array_compare.length - i_max - 1;

puts i_max; # prints: 2

SEE ALSO:

Array.include? is relatively slow. Also, if you need a hash for lookup only, consider using a set: https://stackoverflow.com/a/411164/967621

More about speed comparisons for arrays, sets, and hashes (with benchmarks): Advantages of Set in ruby

Upvotes: 2

iGian
iGian

Reputation: 11183

If I understand properly,

extract the 'highest' index from the array_compare array, if the value exists in the input array,

this could be an option:

array_compare.map.with_index { |e, id| id if array_input.include? e }.compact.max #=> 2

If array_compare = %w[one two three four five three], it returns 5.

Upvotes: 2

user1934428
user1934428

Reputation: 22225

I just executed (copy&paste) your code in irb, and val was "three" afterwards. Did you actually inspect val afterwards, i.e.

p val

? Here is a screenshot of my transscript.

Upvotes: 0

Related Questions