2017-03-23 2 views
0

Ich habe ein Array von Hashes wie folgt aus:Summarize ein Array von Hashes mit einem Kreuztabellen- (auch eine 'Drehtisch' genannt)

my_array_of_hashes = [ 
    { :customer=>"Matthew", 
    :fruit=>"Apples", 
    :quantity=>2, 
    :order_month => "January" 
    }, 
    { :customer => "Philip", 
    :fruit => "Oranges", 
    :quantity => 3, 
    :order_month => "July" 
    }, 
    { :customer => "Matthew", 
    :fruit => "Oranges", 
    :quantity => 1, 
    :order_month => "March" 
    }, 
    { :customer => "Charles", 
    :fruit => "Pears", 
    :quantity => 3, 
    :order_month => "January" 
    }, 
    { :customer => "Philip", 
    :fruit => "Apples", 
    :quantity => 2, 
    :order_month => "April" 
    }, 
    { :customer => "Philip", 
    :fruit => "Oranges", 
    :quantity => 1, 
    :order_month => "July" 
    } 
] 

die Ich mag würde in einem Format row-column zusammenzufassen. Unter Verwendung meiner Beispieldaten würde dies das Summieren der Werte :quantity bedeuten, mit einer Zeile pro einmaligem Kunden, einer Spalte pro einmaligem Obst.

----------------------------------- 
Customer | Apples | Oranges | Pears 
Charles |  |   | 3    
Matthew | 2 | 1 | 
Philip | 2 | 4 | 
----------------------------------- 

Das fühlt sich an wie etwas auflösbar Ruby enumerables aber ich kann nicht sehen, wie.

+0

Haben Sie etwas versuchen? –

+0

Es lohnt sich, [terminal-table gem] (https://github.com/tj/terminal-table) zu betrachten, was sicherlich dazu beitragen könnte, die Tabelle mit einer Ausgabe zu erstellen. – DiodonHystrix

Antwort

3

Arrays erstellen erforderlich, um die Tabelle

I wird konstruieren drei Anordnungen zu konstruieren, die Zeilenbeschriftungen (customers), Spaltenbeschriftungen (fruit) und die Werte in der Tabelle (values) enthalten.

arr_of_hash = [ 
    {:customer=>"Matthew", :fruit=>"Apples", :quantity=>2, :order_month=>"January"}, 
    {:customer=>"Philip", :fruit=>"Oranges", :quantity=>3, :order_month=>"July" }, 
    {:customer=>"Matthew", :fruit=>"Oranges", :quantity=>1, :order_month=>"March" }, 
    {:customer=>"Charles", :fruit=>"Pears", :quantity=>3, :order_month=>"January"}, 
    {:customer=>"Philip", :fruit=>"Apples", :quantity=>2, :order_month=>"April" }, 
    {:customer=>"Philip", :fruit=>"Oranges", :quantity=>1, :order_month=>"July" } 
] 

customers = arr_of_hash.flat_map { |g| g[:customer] }.uniq.sort 
    #=> ["Charles", "Matthew", "Philip"] 
fruit = arr_of_hash.flat_map { |g| g[:fruit] }.uniq.sort 
    #=> ["Apples", "Oranges", "Pears"] 
h = customers.each_with_object({}) { |cust,h| h[cust] = fruit.product([0]).to_h } 
    #=> {"Charles"=>{"Apples"=>0, "Oranges"=>0, "Pears"=>0}, 
    # "Matthew"=>{"Apples"=>0, "Oranges"=>0, "Pears"=>0}, 
    # "Philip" =>{"Apples"=>0, "Oranges"=>0, "Pears"=>0}} 
arr_of_hash.each do |g| 
    customer = g[:customer] 
    h[customer][g[:fruit]] += g[:quantity] 
end 
values = h.map { |_,v| v.values } 
    #=> [[0, 0, 3], 
    # [2, 1, 0], 
    # [2, 4, 0]] 

Beachten Sie, dass unmittelbar vor values = h.map { |_,v| v.values }:

h #=> {"Charles"=>{"Apples"=>0, "Oranges"=>0, "Pears"=>3}, 
    #  "Matthew"=>{"Apples"=>2, "Oranges"=>1, "Pears"=>0}, 
    #  "Philip" =>{"Apples"=>2, "Oranges"=>4, "Pears"=>0}} 

Drucken Sie die Tabelle

def print_table(row_labels_title, row_labels, col_labels, values, gap_size=3) 
    col_width = [values.flatten.max.size, col_labels.max_by(&:size).size].max + gap_size 
    row_labels_width = [row_labels_title.size, row_labels.max_by(&:size).size].max + 
    gap_size 
    horiz_line = '-'*(row_labels_width + col_labels.size * col_width + col_labels.size) 
    puts horiz_line 
    print row_labels_title.ljust(row_labels_width) 
    col_labels.each do |s| 
    print "|#{s.center(col_width)}" 
    end 
    puts 
    row_labels.each do |row_label| 
    print row_label.ljust(row_labels_width) 
    vals = values.shift 
    col_labels.each do |col_label| 
     print "|#{vals.shift.to_s.center(col_width)}" 
    end 
    puts 
    end 
    puts horiz_line 
end 

print_table("Customers", customers, fruit, values, 2) 
-------------------------------------------- 
Customers | Apples | Oranges | Pears 
Charles | 0  | 0  | 3  
Matthew | 2  | 1  | 0  
Philip  | 2  | 4  | 0  
-------------------------------------------- 
+0

.to_h in Zeile 14, "h = customers.each_with ..." wirft für mich einen undefinierten Methodenfehler auf. –

+0

Tom, ich kann diesen Fehler nicht reproduzieren. Es funktioniert gut für mich. Ich benutze Ruby 2.4.0, aber ich glaube nicht, dass es ein Versions-Problem wäre. –

+0

[.to_h ist seit 2.1 verfügbar] (http://stackoverflow.com/a/31684031/6150184) – DiodonHystrix

1

Sie einen Hash mit einem Standardwert von einem Hash mit einem Standardwert von 0 verwenden können :):

fruits = Hash.new { |h, k| h[k] = Hash.new(0) } 
fruits[:some_name] 
# {} 
p fruits[:some_name][:some_fruit] 
# 0 

Auf diese Weise brauchen Sie keine Logik in der Schleife, durchlaufen Sie gerade über die Hash-Werte und fügen Sie die Mengen:

my_array_of_hashes = [ {:customer=>"Matthew", :fruit=>"Apples", :quantity=>2, :order_month => "January"}, {:customer => "Philip", :fruit => "Oranges", :quantity => 3, :order_month => "July"}, {:customer => "Matthew", :fruit => "Oranges", :quantity => 1, :order_month => "March"}, {:customer => "Charles", :fruit => "Pears", :quantity => 3, :order_month => "January"}, {:customer => "Philip", :fruit => "Apples", :quantity => 2, :order_month => "April"}, {:customer => "Philip", :fruit => "Oranges", :quantity => 1, :order_month => "July"} ] 

fruits = Hash.new { |h, k| h[k] = Hash.new(0) } 

my_array_of_hashes.each do |hash| 
    fruits[hash[:customer]][hash[:fruit]] += hash[:quantity] 
end 

p fruits 
# { 
# "Matthew"=>{"Apples"=>2, "Oranges"=>1}, 
# "Philip"=>{"Oranges"=>4, "Apples"=>2}, 
# "Charles"=>{"Pears"=>3} 
# } 
Verwandte Themen