2009-03-02 7 views
10

Je sais que c'est probablement mort simple, mais j'ai des données telles que ce dans un seul fichier:Rembourrage sortie imprimée des données tabulaires

Artichoke 

Green Globe, Imperial Star, Violetto 

24" deep 

Beans, Lima 

Bush Baby, Bush Lima, Fordhook, Fordhook 242 

12" wide x 8-10" deep 

que je voudrais être en mesure de mettre en forme dans un beau type de table TSV, regarder quelque chose comme ceci:

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

Répondre

7

Ceci est un exemple assez complet qui suppose les conditions suivantes

  • Votre liste de produits est contenu dans un fichier appelé veg.txt
  • Vos données sont disposées sur trois lignes par enregistrement avec les champs sur les lignes consécutives

Je suis un peu un noob aux rails donc il existe des moyens sans doute mieux et plus élégant de le faire

#!/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 

Le fichier exemple suivant

 
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 

ayant généré le résultat suivant

 
     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

Nice, Sweet61. Je vais juste changer le nom de la classe Vegetable à TableFormatter ou similaire et ensuite je vais avoir un système de classe réutilisable. À votre santé! andy –

+0

Je pensais à cela par la suite et vous pouviez surcharger to_s dans la classe Vegetable pour faire l'impression alors la boucle à la fin serait beaucoup plus simple –

3

J'ai une petite fonction pour imprimer un tableau 2D sous forme de tableau. Chaque ligne doit avoir le même nombre de colonnes pour que cela fonctionne. Il est également facile de modifier vos besoins.

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

j'ai écrit un petit bijou à faire exactement cela: http://tableprintgem.com

+0

Cela semble très bien, merci! – Alex

+0

Superbe bijou, merci de l'avoir fait. Fait exactement ce dont j'ai besoin! –

+0

Cool gem, mais mes données sont juste dans un tableau. Peut-il être fait? –

16

Personne n'a mentionné le plus « cool »/moyen le plus compact - en utilisant l'opérateur % - par exemple: "%10s %10s" % [1, 2]. Voici un code:

 
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 } 

donne:

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

Voici le code rendu plus lisible:

 
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

C'est une excellente solution. J'ai noté cependant que le '' 'm = xs.map {| _ | _.longueur} '' ' le code ne fonctionne que dans cet exemple par coïncidence. le _.length dans cet exemple retourne réellement le nombre d'éléments dans le tableau. En supposant qu'il y ait toujours au moins 1 élément et que tous les éléments aient le même nombre d'entrées, vous voudrez vraiment '' 'm = xs [0] .map {| _ | _.length} '' ' –

+0

Celui-ci se démarque vraiment. Cependant, nous ne devrions pas calculer max_lengths en fonction du nombre de lignes, mais du nombre de colonnes. Ce que je veux dire par là, au lieu de: 'max_lengths = xs.map {| _ | _.length} ', nous devrions faire:' max_lengths = Array.new (xs [0] .length, 0) ' – Surya