Ruby: confronta gli array e ottieni l'indice in base alla condizione

Aug 18 2020

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 è 2come threeesiste nell'array di input e comparenell'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

1 CarySwoveland Aug 19 2020 at 02:30

Si potrebbe scrivere

array_compare.rindex { |e| array_input.include?(e) }
  #=> 2

ma ciò richiede una ricerca lineare di array_inputper 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 .

2 iGian Aug 18 2020 at 22:14

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.

2 TimurShtatland Aug 18 2020 at 23:18

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_indexrestituisce 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

1 TeWu Aug 19 2020 at 18:12

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_indexpiù 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_indexsolo 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.

user1934428 Aug 18 2020 at 18:32

Ho appena eseguito (copia e incolla) il tuo codice in irb , ed valera "tre" dopo. Hai effettivamente ispezionato in valseguito, ad es

p val

? Ecco uno screenshot della mia trascrizione .