Ruby - сравнивайте массивы и получайте индекс по условию

Aug 18 2020

У меня есть два массива

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.

Ответы

1 CarySwoveland Aug 19 2020 at 02:30

Можно было написать

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 .

2 iGian Aug 18 2020 at 22:14

Если я правильно понимаю,

извлечь «наивысший» индекс из массива 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.

2 TimurShtatland Aug 18 2020 at 23:18

Преобразуйте массив, используемый для поиска, в набор для более быстрого поиска.
Итерируйте с конца массива, где вы ищете максимальный индекс (как вы правильно делаете), а также для скорости. Решения, которые повторяются с начала массива и выбирают максимальный индекс, обычно работают медленнее из-за всех бесполезных поисков, пока не будет найден последний соответствующий элемент. Приведенный ниже метод останавливается быстро - при первом успешном совпадении.
Наконец, исправьте максимальный индекс, так как 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

1 TeWu Aug 19 2020 at 18:12

Самое эффективное решение, которое я мог придумать:

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

Возможно, вы захотите увидеть этот тест различных решений этой проблемы.

user1934428 Aug 18 2020 at 18:32

Я только что выполнил (скопировал и вставил ) ваш код в irb , а valпотом получил "три". Вы действительно valпосле этого осматривали , т.е.

p val

? Вот скриншот моего транскрипта .