2015-01-18 12 views
6

Sto lavorando con una serie di piazzole midi, che assomiglia a questo ...Rubino array - come posso fare valori persistono nel nulla Valori

pitches = [ 
    60, nil, nil, nil, 67, nil, nil, nil, 
    nil, nil, nil, nil, nil, nil, nil, nil, 
    nil, nil, nil, nil, nil, nil, nil, nil, 
    nil, nil, nil, nil, nil, nil, nil, nil 
] 

In questo caso, il passo è ancora 60 su indici 1, 2 e 3.

seguente indice 4, il passo è ancora 67.

Come posso scrivere un metodo per identificare il valore non-nil precedente?

L'unico modo che posso pensare di fare al momento sembra un po 'goffo:

def pitch_at_step(pitches,step) 
    if pitches.any? 
    x = pitches[step] 
    until x != nil 
     index -= 1 
     x = pitches[step]   
    end 
    x 
    else 
    nil 
    end 
end 

Il risultato atteso è nel formato:

pitch_at_step(pitches, 0) # 60 
pitch_at_step(pitches, 2) # 60 
pitch_at_step(pitches, 4) # 67 
pitch_at_step(pitches, 8) # 67 

È questa la soluzione migliore? c'è un modo più ordinato e/o più efficiente?

+0

si può dare anche l'uscita? In modo che non abbiamo bisogno di eseguire il codice. –

+0

Dovrebbe essere il valore non più recente, quindi 0: 60, 1: 60, 2: 60, 3: 60, 4: 67, 5: 67, 6: 67, ecc. – AJFaraday

risposta

4

Se array non è grande si può usare qualcosa di simile:

pitches[0..index].compact.last 

Questo sembra più ordinato, ma non è buono come la vostra per i grandi array di dati

+0

Questo sembra buono, come succede l'app è basata su cicli brevi che l'utente può modificare. Sarei sorpreso se superassero 64 passaggi. – AJFaraday

1

Per ottenere un valore:

value = index.downto(0){|i| pitches[i] and break pitches[i] } 

di calcolare tutti i valori:

values = (y = nil) || pitches.map{|x| x ? y = x : y } 
1

Questo può essere ottenuto anche con Enumerable#chunk:

tmp = nil 
pitches.chunk {|el| tmp = el unless el.nil?; tmp }. 
    map {|val, ar| [val] * ar.size }.flatten 
# => [60, 60, 60, 60, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 
#  67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67] 
2

Ecco un modo semplice per costruire la serie convertito:

last = pitches.first 
pitches[1..-1].map { |i| curr = i || last; last = curr; curr } 
       .unshift(pitches.first) 
    #=> [60, 60, 60, 60, 67,... 67] 

Il PO non ha detto se il primo elemento di pitches è sempre non-nil . Supponiamo che:

pitches = [nil, nil, 61, nil, nil, 60, nil] 

Il metodo di cui sopra sarebbe tornato:

[nil, nil, 61, 61, 61, 60, 60] 

che è quello che vorremmo. Alcune delle altre risposte inciampano quando pitches[step] = nil e pitches[i] = nil per tutto i < step (step è l'indice dell'elemento specificato di pitches).

+0

Un buon punto, la risposta corretta sarà che l'array viene letto come uno schema ripetuto. Il pitch rimarrà come ultimo valore impostato. TUTTAVIA - c'è un bit mask che definisce quando iniziare a suonare il tono, il primo membro del quale sarà sullo stesso punto del primo pitch definito, quindi non c'è bisogno di preoccuparsi di nils all'inizio dell'array – AJFaraday

2

Se si dispone di array di grandi dimensioni che sono principalmente nil, perché non utilizzare invece un hash, memorizzare solo i valori non nulli? E tu guardi le chiavi. (Qui, una versione non ottimizzata)

pitches = {0 => 60, 4 => 67} 

def find(idx) 
    lower = pitches.keys.select { |i| i <= idx} 
    return pitches[lower.sort.last] 
end 

Se le prestazioni sono un problema, è possibile tenere traccia delle chiavi ordinate.

+0

Questo è una buona idea e potrebbe anche risolvere un problema con cui probabilmente mi trovo a correre con il trasferimento dei dati tramite JSON. Significa tuttavia che dovrò implementare questa soluzione in javascript sul client. – AJFaraday

3
pitches.slice_before(&:itself).flat_map{|a| a.fill(a.first)} 
# => [60, 60, 60, 60, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 
#  67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67] 
+0

Kernel # stesso disponibile solo dalla 2.2.0 in poi. (Questa risposta mi ha sicuramente insegnato una cosa o due ...) –

+0

Leggermente meno elegante ma per i rubini precedenti sembra che si possa usare 'slice_before {| x | x! = nil} ' –

0

credo che questo sia un buon modo per farlo in Ruby:

pitches[0..index].reverse_each.find{|x|x}