2012-02-06 11 views
5

Desidero inserire uno spazio ad ogni terzo carattere durante la formattazione di un numero. In base a questa specifica:Formattazione di un numero da dividere ad ogni terza cifra

it "should format an amount" do 
    spaces_on(1202003).should == "1 202 003" 
    end 

e sono arrivato fino a questo pezzo di codice che fa il lavoro

def spaces_on amount 
    thousands = amount/1000 
    remainder = amount % 1000 
    if thousands == 0 
     "#{remainder}" 
    else 
     zero_padded_remainder = '%03.f' % remainder 
     "#{spaces_on thousands} #{zero_padded_remainder}" 
    end 
    end 

Quindi la mia domanda è se questo era il modo migliore per farlo. Sospetto che possa esserci un modo regolare per aggirarlo ma non sono sicuro che mi piacerà la leggibilità di ciò. (D'altra parte - la magia% 03.f non è molto leggibile sia ....)

risposta

5
>> def spaces_on number 
>> number.to_s.gsub(/\D/, '').reverse.gsub(/.{3}/, '\0 ').reverse 
>> end 
=> nil 
>> spaces_on 12345678 
=> "12 345 678" 

Forse c'è un argomento che le espressioni regolari non sono incredibilmente leggibile, ma personalmente penso che siano più semplici per capire che dover pensare in ricorsione.

+0

+1 si presero la risposta giusta dalla mia mente! =) – maerics

+0

bel pezzo. Trovo che molti sviluppatori non conoscano affatto la regex, il che mi evita, ma lo stesso vale probabilmente anche per la ricorsione! Punto preso. – froderik

+0

Ovviamente qui la magia non è l'espressione regolare - una volta che conosci l'obiettivo del metodo, è facile individuare il bit "aggiungendo spazi ogni 3 caratteri". Il 'reverse' /' reverse' è l'unico vero salto mentale. – Gareth

3

Questo binario è?

number_with_delimiter(@number, :delimiter => ' ') 

Questo viene da ActionView::Helpers::NumberHelper.

Se si desidera una versione standalone che funziona con i numeri negativi e punto decimale, utilizzare questo:

def spaces_on number 
    parts = number.to_s.split('.') 
    parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1 ") 
    parts.join(".") 
end 

La logica nel regex è questo: Cattura ogni cifra che viene seguita solo da gruppi di 3 cifre (senza cifre rimanenti), e l'output con lo spazio.

+0

Il tuo errore per '123.4567', che produce' "123.4 567" '. Tuttavia, +1 per menzionare la versione di Rails, in cui è possibile visualizzare il codice sorgente. (Una volta buttato via il "fluff" di i18n, il nucleo della divisione su '.', usando' gsub! 'Nella metà intera e' joininging è di nuovo elegante). – Phrogz

+0

Buona cattura. Ho aggiornato il mio per imitare l'aiutante. Dovrebbe funzionare in quel caso ora. –

5

Una piccola modifica della soluzione di Gareth (un'espressione regolare più complessa, ma nessuna stringa retromarcia):

def spaces_on number 
    number.to_s.gsub(/\d(?=(...)+$)/, '\0 ') 
end 

Funziona per numeri negativi troppo (ma non funziona per galleggianti).

5

def spaces_on(number,sep=" ") 
    number.to_s.tap do |s| 
    :go while s.gsub!(/^([^.]*)(\d)(?=(\d{3})+)/, "\\1\\2#{sep}") 
    end 
end 

NUMBERS = [ 1, 12, 123, 1234, 12345, 123456, 1234567, 
      1.0, 1.2, 1.23, 1.234, 1.2345, 1.23456, 1.234567, 
      12.3, 12.34, 12.345, 12.3456, 
      123.4, 123.45, 123.456, 123.4567, 
      1234.5, 1234.5, 1234.56, 1234.567, 1234.5678 ] 

NUMBERS.each do |n| 
    puts "%10s: %s" % [ n, spaces_on(n).inspect ] 
    puts "%10s: %s" % [ -n, spaces_on(-n).inspect ] 
end 

produce:

  1: "1" 
     -1: "-1" 
     12: "12" 
     -12: "-12" 
     123: "123" 
     -123: "-123" 
     1234: "1 234" 
    -1234: "-1 234" 
    12345: "12 345" 
    -12345: "-12 345" 
    123456: "123 456" 
    -123456: "-123 456" 
    1234567: "1 234 567" 
    -1234567: "-1 234 567" 
     1.0: "1.0" 
     -1.0: "-1.0" 
     1.2: "1.2" 
     -1.2: "-1.2" 
     1.23: "1.23" 
    -1.23: "-1.23" 
    1.234: "1.234" 
    -1.234: "-1.234" 
    1.2345: "1.2345" 
    -1.2345: "-1.2345" 
    1.23456: "1.23456" 
    -1.23456: "-1.23456" 
    1.234567: "1.234567" 
-1.234567: "-1.234567" 
     12.3: "12.3" 
    -12.3: "-12.3" 
    12.34: "12.34" 
    -12.34: "-12.34" 
    12.345: "12.345" 
    -12.345: "-12.345" 
    12.3456: "12.3456" 
    -12.3456: "-12.3456" 
    123.4: "123.4" 
    -123.4: "-123.4" 
    123.45: "123.45" 
    -123.45: "-123.45" 
    123.456: "123.456" 
    -123.456: "-123.456" 
    123.4567: "123.4567" 
-123.4567: "-123.4567" 
    1234.5: "1 234.5" 
    -1234.5: "-1 234.5" 
    1234.5: "1 234.5" 
    -1234.5: "-1 234.5" 
    1234.56: "1 234.56" 
    -1234.56: "-1 234.56" 
    1234.567: "1 234.567" 
-1234.567: "-1 234.567" 
1234.5678: "1 234.5678" 
-1234.5678: "-1 234.5678" 
+0

yay - entrare nello spazio profondo delle espressioni regolari! – froderik

+0

Nei documenti ActionView la cosa che si chiama 'sep' è detta delimitatore. separatore viene utilizzato per il carattere tra le cifre frazionarie e quelle intere. (Non ho un'opinione su cosa è meglio, ma all'inizio ero confuso.) Altrimenti +1 – steenslag

+0

Oh, il mio tentativo è stato chiamato 'to_s_with_delim', definito su' Numeric', ecco perché ho notato.'spaces_on' non è un ottimo nome del metodo IMHO. Ma ancora +1 :) – steenslag