2015-10-22 13 views
9

Come si ordina un elenco di versioni in Ruby? Ho visto cose di tipo naturale, ma questo è un passo oltre.Ordinamento versione (con alfa, beta, ecc.) In rubino

ingresso è un mucchio di stringhe in questo modo:

input = ['10.0.0b12', '10.0.0b3', '10.0.0a2', '9.0.10', '9.0.3'] 

ho quasi posso farlo con la naturally gemma:

require 'naturally' 
Naturally.sort(input) 
=> ["9.0.3", "9.0.10", "10.0.0a2", "10.0.0b12", "10.0.0b3"]  

Problema: 10.0.0b3 è ordinato dopo 10.0.0b12; 10.0.0b3 dovrebbe essere il primo.

Qualcuno ha un modo che funziona? Anche altre lingue sono utili!

+1

Queste stringhe di versione estremamente atipiche. Non è probabile che tu trovi una soluzione pronta per l'uso; dovresti scrivere il tuo. – meagar

+0

L'ho cambiato per usare il Versioning semantico. Questo è più tipico. –

+0

@MusashiAharon Questo non è abbastanza Versioning semantico. Per questo si vorrebbe che il 'b12'' b3', ecc sia preceduto da un '-'. – Ajedi32

risposta

19

navi rubino con la classe Gem, che conosce le versioni:

ar = ['10.0.0b12', '10.0.0b3', '10.0.0a2', '9.0.10', '9.0.3'] 

p ar.sort_by { |v| Gem::Version.new(v) } 
# => ["9.0.3", "9.0.10", "10.0.0a2", "10.0.0b3", "10.0.0b12"] 
+2

Bello. Per quello che vale - sembra che questo maneggi solo "alpha" e "beta". Cioè, '['9.0.10rc2', '9.0.10', '9.0.10rc1', '9.0.10a', '9.0.10test']' produce '[" 9.0.10a "," 9.0.10rc1 " , "9.0.10rc2", "9.0.10 test", "9.0.10"] '. Dovrebbe essere sufficiente, dal momento che "alpha/beta/pre-release/rc/release" capita di fluire in ordine alfabetico in ogni caso, ma potrebbe essere strano se i tuoi dati si discostano troppo da quello. – DreadPirateShawn

+0

piuttosto interessante come funziona: https://github.com/rubygems/rubygems/blob/1aa8033952d4eda5ca131039822f9548166ab507/lib/rubygems/version.rb#L336-L361 – Anthony

1

Se si interpreta questo come "ordina per ogni segmento di cifre", allora il seguente sarà gestire il vostro input esempio di cui sopra:

input.map{ |ver| ver.split(%r{[^\d]+}).map(&:to_i) }.zip(input).sort.map(&:last) 
=> ["9_0", "9_1", "10_0b3", "10_0b12"] 

Cioè,

  • per ogni valore, ad esempio, 10_0b3
  • parti separate su qualsiasi lunghezza di caratteri non numerici, ad esempio ["10","0","3"]
  • Cast ciascun segmento cifre a intero, ad esempio [10,0,3]
  • zip con ingresso originale, rese [[[10, 0, 12], "10_0b12"], [[10, 0, 3], "10_0b3"], [[9, 0], "9_0"], [[9, 1], "9_1"]]
  • ordinamento, in virtù della [10,0,3] < [10,0,12]
  • ottengono ultimo valore di ogni elemento, che è il valore di ingresso originale corrispondente a ciascun valore sortable trasformati

Ora concesso, questo è ancora abbastanza personalizzato - i numeri di versione semplici come "9_0a" vs "9_0b" non saranno gestiti, entrambi sembreranno essere [9,0] - quindi potrebbe essere necessario modificarlo ulteriormente, ma si spera questo ti fa iniziare un percorso percorribile.

EDIT: ingresso esempio di cui sopra è cambiato, così ho cambiato la regex per assicurarsi che la cifra-matching è avido, e con che tiene ancora in piedi:

irb(main):018:0> input = ['10.0.0b12', '10.0.0b3', '9.0.10', '9.0.3'] 
=> ["10.0.0b12", "10.0.0b3", "9.0.10", "9.0.3"] 
irb(main):025:0> input.map{ |ver| ver.split(%r{[^\d]+}).map(&:to_i) }.zip(input).sort.map(&:last) 
=> ["9.0.3", "9.0.10", "10.0.0b3", "10.0.0b12"] 
+0

Questo aiuta, ma i beta non sono l'unico suffisso possibile. Potremmo anche avere alfa come '10 .0.0a2 'o rilasciare candidati come '10 .0.0rc1'. Se questi sono fianco a fianco con gli altri, l'ordinamento si interrompe. –