2009-03-02 11 views
10

So che questo è probabilmente morto semplice, ma ho alcuni dati come questo in un unico file:imbottitura dei dati tabulari

Artichoke 

Green Globe, Imperial Star, Violetto 

24" deep 

Beans, Lima 

Bush Baby, Bush Lima, Fordhook, Fordhook 242 

12" wide x 8-10" deep 

che mi piacerebbe essere in grado di formattare in un bel tipo TSV di tavolo, a guardare qualcosa di simile:

Name | Varieties | Container Data 
----------|------------- |------- 
some data here nicely padded with even spacing and right aligned text 

risposta

7

Questo è un esempio abbastanza completo che presuppone quanto segue

  • L'elenco dei prodotti è contenuta in un file chiamato veg.txt
  • tuoi dati sono disposti su tre righe per record con i campi su righe consecutive

Sono un po 'di un niubbo alle rotaie quindi non ci sono modi sicuramente migliori e più elegante per fare questo file

#!/usr/bin/ruby 

class Vegetable 

    @@max_name ||= 0 
    @@max_variety ||= 0 
    @@max_container ||= 0 

    attr_reader :name, :variety, :container 

    def initialize(name, variety, container) 
    @name = name 
    @variety = variety 
    @container = container 

    @@max_name = set_max(@name.length, @@max_name) 
    @@max_variety = set_max(@variety.length, @@max_variety) 
    @@max_container = set_max(@container.length, @@max_container) 
    end 

    def set_max(current, max) 
    current > max ? current : max 
    end 

    def self.max_name 
    @@max_name 
    end 

    def self.max_variety 
    @@max_variety 
    end 

    def self.max_container() 
    @@max_container 
    end 

end 

    products = [] 


    File.open("veg.txt") do | file| 

     while name = file.gets 
     name = name.strip 
     variety = file.gets.to_s.strip 
     container = file.gets.to_s.strip 
     veg = Vegetable.new(name, variety, container) 
     products << veg 
     end 
    end 

    format="%#{Vegetable.max_name}s\t%#{Vegetable.max_variety}s\t%#{Vegetable.max_container}s\n" 
    printf(format, "Name", "Variety", "Container") 
    printf(format, "----", "-------", "---------") 
    products.each do |p| 
     printf(format, p.name, p.variety, p.container) 
    end 

Il seguente esempio di

 
Artichoke 
Green Globe, Imperial Star, Violetto 
24" deep 
Beans, Lima 
Bush Baby, Bush Lima, Fordhook, Fordhook 242 
12" wide x 8-10" deep 
Potatoes 
King Edward, Desiree, Jersey Royal 
36" wide x 8-10" deep 

ha prodotto il seguente output

 
     Name          Variety    Container 
     ----          -------    --------- 
    Artichoke   Green Globe, Imperial Star, Violetto     24" deep 
Beans, Lima Bush Baby, Bush Lima, Fordhook, Fordhook 242 12" wide x 8-10" deep 
    Potatoes   King Edward, Desiree, Jersey Royal 36" wide x 8-10" deep 
+0

Bello, Sweet61. Cambierò semplicemente il classname Vegetable in TableFormatter o simile e poi avrò un sistema di classi riutilizzabile. Saluti! andy –

+0

Ho pensato a questo in seguito e potresti escludere to_s nella classe Vegetable per fare la stampa, quindi il loop alla fine sarebbe molto più semplice –

3

Ho una piccola funzione per stampare un array 2D come tabella. Ogni riga deve avere lo stesso numero di colonne affinché funzioni. È anche facile da modificare per le tue esigenze.

def print_table(table) 
    # Calculate widths 
    widths = [] 
    table.each{|line| 
    c = 0 
    line.each{|col| 
     widths[c] = (widths[c] && widths[c] > col.length) ? widths[c] : col.length 
     c += 1 
    } 
    } 
    # Indent the last column left. 
    last = widths.pop() 
    format = widths.collect{|n| "%#{n}s"}.join(" ") 
    format += " %-#{last}s\n" 
    # Print each line. 
    table.each{|line| 
    printf format, *line 
    } 
end 
17

ho scritto un gioiello per fare esattamente questo: http://tableprintgem.com

+0

Questo sembra fantastico, grazie! – Alex

+0

Grande gioiello, grazie per averlo fatto. Fa esattamente quello di cui ho bisogno! –

+0

Fantastico gioiello, ma i miei dati sono solo in un array. Può essere fatto? –

16

Nessuno ha menzionato il/modo più compatto "cool" - utilizzando l'operatore % - per esempio: "%10s %10s" % [1, 2]. Ecco il codice:

 
xs = [ 
    ["This code", "is", "indeed"], 
    ["very", "compact", "and"], 
    ["I hope you will", "find", "it helpful!"], 
] 
m = xs.map { |_| _.length } 
xs.each { |_| _.each_with_index { |e, i| s = e.size; m[i] = s if s > m[i] } } 
xs.each { |x| puts m.map { |_| "%#{_}s" }.join(" " * 5) % x } 

Dà:

 
     This code   is   indeed 
      very  compact    and 
I hope you will  find  it helpful! 

Qui è il codice reso più leggibile:

 
max_lengths = xs.map { |_| _.length } 
xs.each do |x| 
    x.each_with_index do |e, i| 
    s = e.size 
    max_lengths[i] = s if s > max_lengths[i] 
    end 
end 
xs.each do |x| 
    format = max_lengths.map { |_| "%#{_}s" }.join(" " * 5) 
    puts format % x 
end 
+0

Questa è un'ottima soluzione. Ho notato tuttavia che il '' 'm = xs.map {| _ | _.length} '' ' Il codice funziona solo in questo esempio per coincidenza. il valore _.length in questo esempio restituisce effettivamente il numero di elementi nell'array. Supponendo che ci sia sempre almeno 1 elemento e tutti gli elementi hanno lo stesso numero di voci, si vorrebbe veramente '' 'm = xs [0] .map {| _ | _.length} '' ' –

+0

Questo spicca davvero. Tuttavia, non dovremmo calcolare il calcolo di max_lengths in base al numero di righe, invece dovrebbe essere sul numero di colonne. Ciò che intendo è, invece di: 'max_lengths = xs.map {| _ | _.length} ', dovremmo fare:' max_lengths = Array.new (xs [0] .length, 0) ' – Surya