2017-07-03 1 views
2

Ich habe eine Reihe von Hashes, wobei die Schlüssel Länder und die Werte die Anzahl der Tage sind.Selektive Ruby-Aggregatwerte innerhalb einer Sammlung von Hashes

Ich möchte über die Hashes aggregieren und die Werte für die Länder, die gleich sind, summieren.

könnte das Array so aussehen countries = [{"Country"=>"Brazil", "Duration"=>731/1 days}, {"Country"=>"Brazil", "Duration"=>365/1 days}]

würde Ich mag das etwas auf den Linien zurückzukehren: [{"Country" => "Brazil", "Duration"=>1096/1 days}]

habe ich versucht, die anderen Fragen auf SO like this one

countries.inject{|new_h, old_h| new_h.merge(old_h) {|_, old_v, new_v| old_v + new_v}} 

Produziert {"Country"=>"BrazilBrazil", "Duration"=>1096/1 days}

Gibt es eine Möglichkeit, selektiv nur bestimmte Werte zu verschmelzen Ue?

Antwort

2

können Sie Ihren Code wie folgt aktualisieren:

countries.inject do |new_h, old_h| 
    new_h.merge(old_h) do |k, old_v, new_v| 
     if k=="Country" then old_v else old_v + new_v end 
    end 
end 
# => {"Country"=>"Brazil", "Duration"=>1096} 

, wo man im Grunde die k verwenden (für Schlüssel) Argument zwischen verschiedenen Verschmelzen Politik zu wechseln.

+0

Legende! Danke @metaphori – TheLegend

4

Dies verwendet die Form Hash::new, die erstellt einen leeren Hash mit einem Standardwert erstellt (hier 0). Für einen auf diese Weise erstellten Hash h gibt h[k] den Standardwert zurück, wenn der Hash keinen Schlüssel k hat. Der Hash wird nicht geändert.

countries = [{"Country"=>"Brazil", "Duration"=>"731/1 days"}, 
      {"Country"=>"Argentina", "Duration"=>"123/1 days"}, 
      {"Country"=>"Brazil", "Duration"=>"240/1 days"}, 
      {"Country"=>"Argentina", "Duration"=>"260/1 days"}] 

countries.each_with_object(Hash.new(0)) {|g,h| h[g["Country"]] += g["Duration"].to_i }. 
    map { |k,v| { "Country"=>k, "Duration"=>"#{v}/1 days" } } 
    #=> [{"Country"=>"Brazil", "Duration"=>"971/1 days"}, 
    # {"Country"=>"Argentina", "Duration"=>"383/1 days"}] 

Der erste Hash vorbei zum Block und g des Blockvariablen zugewiesen.

g = {"Country"=>"Brazil", "Duration"=>"731/1 days"} 

Zu dieser Zeit h #=> {}. Wir berechnen dann

h[g["Country"]] += g["Duration"].to_i 
    #=> h["Brazil"] += "971/1 days".to_i 
    #=> h["Brazil"] = h["Brazil"] + 971 
    #=> h["Brazil"] = 0 + 971 # h["Brazil"] 

Siehe String#to_i für eine Erklärung, warum "971/1 days".to_i kehrt 971.

h["Brazil"] auf der rechten Seite der Gleichheit gibt den Standardwert von 0 weil h nicht (noch) einen Schlüssel "Brazil" haben. Beachten Sie, dass h["Brazil"] auf der rechten Seite ist syntaktischer Zucker für h.[]("Brazil"), während auf der linken Seite ist es syntaktischer Zucker für h.[]=(h["Brazil"] + 97). Es ist Hash#[], die den Standardwert zurückgibt, wenn der Hash den angegebenen Schlüssel nicht hat. Die verbleibenden Schritte sind ähnlich.

+0

'Hash.new (0)' wird die Dauer in Sekunden konvertieren. Sie können versuchen, 'Hash.new (0.days)' zu tun, und dann innerhalb des Kartenblocks können Sie '" Dauer "=> v' – TheLegend

+1

@TheLegend machen, danke für den Vorschlag. Ich kenne Rails nicht, also gab ich eine reine Ruby-Lösung. –