Frage

Was ist die „Rubyist“-Methode, um die folgende Datenstrukturtransformation durchzuführen:

Ich habe

    incoming = [ {:date => 20090501, :width => 2}, 
                 {:date => 20090501, :height => 7}, 
                 {:date => 20090501, :depth => 3}, 
                 {:date => 20090502, :width => 4}, 
                 {:date => 20090502, :height => 6}, 
                 {:date => 20090502, :depth => 2},
               ]

und ich möchte diese bis zum Datum reduzieren, um sie zu erhalten

    outgoing = [ {:date => 20090501, :width => 2, :height => 7, :depth => 3},
                 {:date => 20090502, :width => 4, :height => 6, :depth => 2},
               ]

Ein Array aus Arrays wäre im letzten Schritt auch in Ordnung, vorausgesetzt, dass die Spalten in jeder Zeile in der gleichen Reihenfolge sind.Wichtig ist auch, dass ich nicht alle Hash-Schlüssel im Voraus kenne (das heißt, ich kenne :width, :height oder : Depth nicht – es könnten :cats, :dogs und :hamsters sein).

War es hilfreich?

Lösung

Wenn Sie Ruby 1.8.7 oder Ruby 1.9+ verwenden, liest sich der folgende Code gut:

incoming.group_by{|hash| hash[:date]}.map do |_, hashes| 
  hashes.reduce(:merge)
end

Der Unterstrich in den Blockattributen (_, Hashes) zeigt an, dass wir dieses bestimmte Attribut nicht benötigen/sich darum kümmern.

#reduce ist ein Alias ​​für #inject, das verwendet wird reduzieren eine Sammlung in ein einzelnes Element.In den neuen Ruby-Versionen wird auch ein Symbol akzeptiert, das den Namen der dafür verwendeten Methode darstellt die Ermäßigung.

Es beginnt mit dem Aufruf der Methode für das erste Element in der Sammlung mit dem zweiten Element als Argument.Anschließend wird die Methode erneut für das Ergebnis mit dem dritten Element als Argument aufgerufen usw., bis keine Elemente mehr vorhanden sind.

[1, 3, 2, 2].reduce(:+) => [4, 2, 2] => [6, 2] => 8

Andere Tipps

Hier ist ein Motto:)

incoming.inject({}){ |o,i| o[i[:date]]||=[];o[i[:date]]<<i;o}.map{|a| a[1].inject(){|o,i| o.merge(i)}}

Aber eigentlich die frühere Post ist klar, und vielleicht schneller sein.

EDIT: mit einem bisschen Optimierung:

p incoming.inject(Hash.new{|h,k| h[k]=[]}){ |o,i| o[i[:date]]<<i;o}.map{|a| a[1].inject(){|o,i| o.merge(i)}}

Eine kurze Lösung:

incoming = [ {:date => 20090501, :width => 2}, 
             {:date => 20090501, :height => 7}, 
             {:date => 20090501, :depth => 3}, 
             {:date => 20090502, :width => 4}, 
             {:date => 20090502, :height => 6}, 
             {:date => 20090502, :depth => 2},
           ]

temp = Hash.new {|hash,key| hash[key] = {}}
incoming.each {|row| temp[row[:date]].update(row)}
outgoing = temp.values.sort {|*rows| rows[0][:date] <=> rows[1][:date]}

Das einzige, was überhaupt heikel ist hier der Hash-Konstruktor, mit dem Sie einen Block liefern erlaubt, die aufgerufen wird, wenn Sie einen nicht vorhandenen Schlüssel zugreifen. Also ich habe die Hash eine leere Hash erstellen für uns mit den Werten zu aktualisieren, wir finden. Dann habe ich nur das Datum als den Hash-Schlüssel verwenden, sortieren Sie die Hash-Werte von Datum und wir umgewandelt.

Versuchen Sie folgendes:

incoming = [ {:date => 20090501, :width => 2}, 
                 {:date => 20090501, :height => 7}, 
                 {:date => 20090501, :depth => 3}, 
                 {:date => 20090502, :width => 4}, 
                 {:date => 20090502, :height => 6}, 
                 {:date => 20090502, :depth => 2},
               ]

# Grouping by `:date`
temp = {}

incoming.each do |row|
    if temp[row[:date]].nil? 
        temp[row[:date]] = []
    end

    temp[row[:date]] << row
end      

# Merging it together
outcoming = []         

temp.each_pair do |date, hashlist|
    res = {}
    hashlist.each do |hash|
        res.merge!(hash)
    end
    outcoming << res 
end

Weitere Informationen über die hash-Mitglieder finden Sie unter dieser Seite

Bei der Bestellung wichtig ist, müssen Sie gezackte Arrays verwenden:

incoming = [ {:date => 20090501, :width => 2}, 
                 {:date => 20090501, :height => 7}, 
                 {:date => 20090501, :depth => 3}, 
                 {:date => 20090502, :width => 4}, 
                 {:date => 20090502, :height => 6}, 
                 {:date => 20090502, :depth => 2},
               ]

# Grouping by `:date`
temp = {}

incoming.each do |row|
    if temp[row[:date]].nil? 
        temp[row[:date]] = []
    end
    key = row[:date]
    row.delete :date
    temp[key] << row
end      

# Merging it together
outcoming = []         

temp.each_pair do |date, hashlist|
    res = [:date, date]
    hashlist.each do |hash|
        hash.each_pair {|key, value| res << [key, value] }
    end
    outcoming << res
end
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top