Ruby: confronta gli array e ottieni l'indice in base alla condizione
Ho due array
array_input = %w[one two three two]
array_compare = %w[one two three four five]
Voglio estrarre l'indice 'più alto' dall'array array_compare, se il valore esiste nell'array di input. L'output desiderato è 2
come three
esiste nell'array di input e compare
nell'array.
ho provato
val = nil
array_compare.reverse_each do |v|
val = v and break if array_input.include? v
end
ma non imposta val.
Risposte
Si potrebbe scrivere
array_compare.rindex { |e| array_input.include?(e) }
#=> 2
ma ciò richiede una ricerca lineare di array_input
per ogni elemento di array_compare
(a partire dall'ultimo) finché non viene trovata una corrispondenza. Meglio è il seguente.
array_compare.rindex((array_compare & array_input).last)
#=> 2
I passi sono come segue.
a = array_compare & array_input
#=> ["one", "two", "three"]
Vedi Array # & . Notare che "L'ordine viene mantenuto dall'array originale [ array_compare
].". Questa operazione in un passaggio sarà molto veloce poiché è implementata in C. Continuando,
e = a.last
#=> "three"
array_compare.rindex(e)
#=> 2
Vedi Array # rindex .
Se ho capito bene,
estrae l'indice 'più alto' dall'array array_compare, se il valore esiste nell'array di input,
questa potrebbe essere un'opzione:
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]
, ritorna 5
.
Converti l'array utilizzato per le ricerche in un set , per una ricerca più rapida.
Itera dalla fine dell'array dove stai cercando l'indice massimo (come stai facendo correttamente), anche per la velocità. Le soluzioni che iterano dall'inizio dell'array e scelgono l'indice massimo sono generalmente più lente a causa di tutte le ricerche inutili fino a quando non viene trovato l'ultimo elemento corrispondente. Il metodo seguente si interrompe rapidamente: alla prima partita riuscita.
Infine, correggi l'indice massimo, poiché array_compare.reverse.each_with_index
restituisce gli indici dell'array invertito .
Il codice risultante potrebbe essere più lungo rispetto a molte altre risposte, ma è sia semplice che veloce:
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
GUARDA ANCHE:
Array.include? è relativamente lento. Inoltre, se hai bisogno di un hash solo per la ricerca, considera l'utilizzo di un set:https://stackoverflow.com/a/411164/967621
Maggiori informazioni sui confronti di velocità per array, set e hash (con benchmark): vantaggi di Set in ruby
La soluzione più performante che ho potuto trovare è:
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
Nota che l'argomento set_input
è a Set, non un Array. La conversione di un array in un set ha senso, ma solo se si desidera chiamare find_last_index
più volte con lo stesso set. Altrimenti il processo di conversione di un array in un set ( to_set
) richiede più tempo di quello che puoi ottenere usando a Set#include?
invece di Array#include?
. Quindi, se vuoi usare find_last_index
solo una volta, non dovresti chiamare find_last_index(array_input.to_set, array_compare)
, ma invece usa questa versione che non usa affatto i set:
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
Potresti voler vedere questo benchmark di diverse soluzioni a questo problema.
Ho appena eseguito (copia e incolla) il tuo codice in irb , ed val
era "tre" dopo. Hai effettivamente ispezionato in val
seguito, ad es
p val
? Ecco uno screenshot della mia trascrizione .