Ruby - compare matrizes e obtenha índice com base na condição
Eu tenho duas matrizes
array_input = %w[one two three two]
array_compare = %w[one two three four five]
Quero extrair o índice 'mais alto' da matriz array_compare, se o valor existir na matriz de entrada. O resultado desejado é 2
que three
existe na matriz de entrada e a compare
matriz.
eu tentei
val = nil
array_compare.reverse_each do |v|
val = v and break if array_input.include? v
end
mas não define val.
Respostas
Alguém poderia escrever
array_compare.rindex { |e| array_input.include?(e) }
#=> 2
mas isso requer uma pesquisa linear de array_input
para cada elemento de array_compare
(começando com o último) até que uma correspondência seja encontrada. Melhor é o seguinte.
array_compare.rindex((array_compare & array_input).last)
#=> 2
As etapas são as seguintes.
a = array_compare & array_input
#=> ["one", "two", "three"]
Consulte Array # & . Observe que "A ordem é preservada do array original [ array_compare
].". Esta operação de uma passagem será muito rápida, pois é implementada em C. Continuando,
e = a.last
#=> "three"
array_compare.rindex(e)
#=> 2
Consulte Array # rindex .
Se bem entendi,
extraia o índice 'mais alto' da matriz array_compare, se o valor existir na matriz de entrada,
isso pode ser uma opção:
array_compare.map.with_index { |e, id| id if array_input.include? e }.compact.max #=> 2
Se array_compare = %w[one two three four five three]
, ele retorna 5
.
Converta a matriz usada para pesquisas em um conjunto , para uma pesquisa mais rápida.
Itere a partir do final do array onde você está procurando o índice máximo (como você está fazendo corretamente), também para velocidade. Soluções que iteram desde o início da matriz e escolhem o índice máximo são geralmente mais lentas devido a todas as pesquisas inúteis até que o último elemento correspondente seja encontrado. O método abaixo para rapidamente - na primeira partida bem-sucedida.
Finalmente, corrija o índice máximo, pois array_compare.reverse.each_with_index
retorna os índices da matriz invertida .
O código resultante pode ser mais longo do que em muitas das outras respostas, mas é simples e rápido:
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
VEJA TAMBÉM:
Array.include? é relativamente lento. Além disso, se você precisar de um hash apenas para pesquisa, considere usar um conjunto:https://stackoverflow.com/a/411164/967621
Mais sobre comparações de velocidade para arrays, sets e hashes (com benchmarks): Vantagens de Set em ruby
A solução de melhor desempenho que eu poderia propor é:
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
Observe que o argumento set_input
é a Set, não um Array. Converter um array em um conjunto faz sentido, mas apenas se você quiser chamar find_last_index
muitas vezes com o mesmo conjunto. Caso contrário, o processo de conversão de um array em set ( to_set
) leva mais tempo do que você pode ganhar usando um em Set#include?
vez de um Array#include?
. Então, se você quiser usar find_last_index
apenas uma vez, você não deve chamar find_last_index(array_input.to_set, array_compare)
, mas sim esta versão que não usa conjuntos:
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
Você pode querer ver este benchmark de diferentes soluções para este problema.
Acabei de executar (copiar e colar) seu código no irb , e fiquei val
"três" depois. Você realmente inspecionou val
depois, ou seja,
p val
? Aqui está uma captura de tela da minha transcrição .