Ruby - сравнивайте массивы и получайте индекс по условию
У меня есть два массива
array_input = %w[one two three two]
array_compare = %w[one two three four five]
Я хочу извлечь «наивысший» индекс из массива array_compare, если значение существует во входном массиве. Желаемый результат такой, 2
как three
есть во входном массиве и в compare
массиве.
я пытался
val = nil
array_compare.reverse_each do |v|
val = v and break if array_input.include? v
end
но он не устанавливает val.
Ответы
Можно было написать
array_compare.rindex { |e| array_input.include?(e) }
#=> 2
но для этого требуется линейный поиск array_input
каждого элемента array_compare
(начиная с последнего), пока не будет найдено совпадение. Лучше следующее.
array_compare.rindex((array_compare & array_input).last)
#=> 2
Шаги следующие.
a = array_compare & array_input
#=> ["one", "two", "three"]
См. Массив # & . Обратите внимание, что «Порядок сохраняется из исходного массива [ array_compare
].». Эта однопроходная операция будет очень быстрой, поскольку она реализована в C. Продолжая,
e = a.last
#=> "three"
array_compare.rindex(e)
#=> 2
См. Массив # rindex .
Если я правильно понимаю,
извлечь «наивысший» индекс из массива array_compare, если значение существует во входном массиве,
это может быть вариант:
array_compare.map.with_index { |e, id| id if array_input.include? e }.compact.max #=> 2
Если array_compare = %w[one two three four five three]
, он возвращается 5
.
Преобразуйте массив, используемый для поиска, в набор для более быстрого поиска.
Итерируйте с конца массива, где вы ищете максимальный индекс (как вы правильно делаете), а также для скорости. Решения, которые повторяются с начала массива и выбирают максимальный индекс, обычно работают медленнее из-за всех бесполезных поисков, пока не будет найден последний соответствующий элемент. Приведенный ниже метод останавливается быстро - при первом успешном совпадении.
Наконец, исправьте максимальный индекс, так как array_compare.reverse.each_with_index
возвращает индексы обратного массива.
Полученный код может быть длиннее, чем во многих других ответах, но он прост и быстр:
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
СМОТРИТЕ ТАКЖЕ:
Array.include? относительно медленно. Кроме того, если вам нужен хеш только для поиска, рассмотрите возможность использования набора:https://stackoverflow.com/a/411164/967621
Подробнее о сравнении скорости массивов, наборов и хэшей (с тестами): преимущества Set в ruby
Самое эффективное решение, которое я мог придумать:
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
Обратите внимание, что это аргумент set_input
, а Setне Array. Преобразование массива в набор имеет смысл, но только если вы хотите вызывать find_last_index
много раз с одним и тем же набором. В противном случае процесс преобразования массива в set ( to_set
) займет больше времени, чем вы можете получить, используя Set#include?
вместо Array#include?
. Поэтому, если вы хотите использовать find_last_index
только один раз, вы не должны вызывать find_last_index(array_input.to_set, array_compare)
, а вместо этого используйте эту версию, которая вообще не использует наборы:
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
Возможно, вы захотите увидеть этот тест различных решений этой проблемы.
Я только что выполнил (скопировал и вставил ) ваш код в irb , а val
потом получил "три". Вы действительно val
после этого осматривали , т.е.
p val
? Вот скриншот моего транскрипта .