Reputation: 175
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
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
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
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
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
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