2016-09-07 2 views
1

Ich habe eine questions Liste, und ich muss sie trennen. Die Beziehung istRails Hash kombinieren durch ihren Wert

Question_set has_many questions 
BookVolume has_many questions 
Subject has_many book_volumes 
Publisher has_many subjects 
Section has_many :questions 

Jetzt habe ich nur setzen questions und ihre relative Modell id, name in hash innerhalb eines array.

data = [] 
question_set.questions.each do |q| 
    data << {publisher: {id: q.publisher.id, name: q.publisher.name}, subject: {id: q.book_volume.subject.id, name: q.book_volume.subject.name}, volume: {id: q.book_volume_id, name: q.book_volume.name}, chapter: [{id: q.section_id, name: q.section.name}]} 
end 

Daher ist die data grundsätzlich

>>data 

[ 
    { 
     :publisher => { 
       :id => 96, 
      :name => "P1" 
     }, 
      :subject => { 
       :id => 233, 
      :name => "S1" 
     }, 
      :volume => { 
       :id => 1136, 
      :name => "V1" 
     }, 
      :chapter => [ 
      { 
        :id => 16155, 
       :name => "C1" 
      } 
     ] 
    }, 
     { 
     :publisher => { 
       :id => 96, 
      :name => "P1" 
     }, 
      :subject => { 
       :id => 233, 
      :name => "S1" 
     }, 
      :volume => { 
       :id => 1136, 
      :name => "V1" 
     }, 
      :chapter => [ 
      { 
        :id => 16158, 
       :name => "C2" 
      } 
     ] 
    } 
] 

jedoch sein wird, möchte ich die chapter kombiniert werden, wenn sie die gleiche publisher, subject und volume Also in diesem Fall, erhalten, wird es sein,

>>data 

[ 
    { 
     :publisher => { 
       :id => 96, 
      :name => "P1" 
     }, 
      :subject => { 
       :id => 233, 
      :name => "S1" 
     }, 
      :volume => { 
       :id => 1136, 
      :name => "V1" 
     }, 
      :chapter => [ 
      { 
        :id => 16155, 
       :name => "C2" 
      }, 
      { 
        :id => 16158, 
       :name => "C2" 
      } 
     ] 
    } 
] 

Antwort

2

-Code

def group_em(data) 
    data.group_by { |h| [h[:publisher], h[:subject], h[:volume]] }. 
     map do |k,v| 
     h = { publisher: k[0], subject: k[1], volume: k[2] } 
     h.update(chapters: v.each_with_object([]) { |f,a| 
      a << f[:chapter] }.flatten) 
     end 
end 

Beispiel

Lassen data gleich der Anordnung von Hashes (das erste Array oben).

group_em(data) 
    #=> [{:publisher=>{:id=>96, :name=>"P1"}, 
    #  :subject=>{:id=>233, :name=>"S1"}, 
    #  :volume=>{:id=>1136, :name=>"V1"}, 
    #  :chapters=>[{:id=>16155, :name=>"C1"}, {:id=>16158, :name=>"C2"}] 
    # } 
    # ] 

Hier data enthält nur zwei Hashes und diese Hashes haben die gleichen Werte für die Schlüssel :publisher, :subject und :volume. Dieser Code ermöglicht dem Array eine beliebige Anzahl von Hashwerten und gruppiert sie nach einem Array der Werte dieser drei Schlüssel, wobei für jede dieser Gruppen ein Hash erzeugt wird. Darüber hinaus sind die Werte des Schlüssels :chapters Arrays, die einen einzelnen Hash enthalten, aber dieser Code erlaubt es, dass das Array mehrere Hashes enthält. (Wenn das Array immer hat genau einen Hash, sollten Sie den Wert von :chapters macht den Hash selbst eher als ein Array mit diesem Hash.)

Erklärung

Siehe Enumerable#group_by und Hash#update (aka Hash#merge!).

Die Schritte sind wie folgt.

h = data.group_by { |h| [h[:publisher], h[:subject], h[:volume]] } 
    #=> { 
    # [{:id=>96, :name=>"P1"}, 
    #  {:id=>233, :name=>"S1"}, 
    #  {:id=>1136, :name=>"V1"} 
    # ]=>[{:publisher=>{:id=>96, :name=>"P1"}, 
    #   :subject=>{:id=>233, :name=>"S1"}, 
    #   :volume=>{:id=>1136, :name=>"V1"}, 
    #   :chapter=>[{:id=>16155, :name=>"C1"}] 
    #  }, 
    #  {:publisher=>{:id=>96, :name=>"P1"}, 
    #   :subject=>{:id=>233, :name=>"S1"}, 
    #   :volume=>{:id=>1136, :name=>"V1"}, 
    #   :chapter=>[{:id=>16158, :name=>"C2"}] 
    #  } 
    #  ] 
    # } 

Das erste Schlüssel-Wert-Paar zugeordnet ist map ‚S-Block und die Blockgrößen werden übergeben.

k,v = h.first 
    #=> [[{:id=>96, :name=>"P1"}, {:id=>233, :name=>"S1"}, {:id=>1136, :name=>"V1"}], 
    # [{:publisher=>{:id=>96, :name=>"P1"}, :subject=>{:id=>233, :name=>"S1"}, 
    #  :volume=>{:id=>1136, :name=>"V1"}, :chapter=>[{:id=>16155, :name=>"C1"}]}, 
    # {:publisher=>{:id=>96, :name=>"P1"}, :subject=>{:id=>233, :name=>"S1"}, 
    #  :volume=>{:id=>1136, :name=>"V1"}, :chapter=>[{:id=>16158, :name=>"C2"}]}]] 
k #=> [{:id=>96, :name=>"P1"}, {:id=>233, :name=>"S1"}, {:id=>1136, :name=>"V1"}] 
v #=> [{:publisher=>{:id=>96, :name=>"P1"}, 
    #  :subject=>{:id=>233, :name=>"S1"}, 
    #  :volume=>{:id=>1136, :name=>"V1"}, 
    #  :chapter=>[{:id=>16155, :name=>"C1"}]}, 
    # {:publisher=>{:id=>96, :name=>"P1"}, 
    #  :subject=>{:id=>233, :name=>"S1"}, 
    #  :volume=>{:id=>1136, :name=>"V1"}, 
    #  :chapter=>[{:id=>16158, :name=>"C2"}]}] 

und die Blockberechnung durchgeführt.

h = { publisher: k[0], subject: k[1], volume: k[2] } 
    #=> {:publisher=>{:id=>96, :name=>"P1"}, 
    # :subject=>{:id=>233, :name=>"S1"}, 
    # :volume=>{:id=>1136, :name=>"V1"} 
    # } 
a = v.each_with_object([]) { |f,a| a << f[:chapter] } 
    #=> [[{:id=>16155, :name=>"C1"}], [{:id=>16158, :name=>"C2"}]] 
b = a.flatten 
    #=> [{:id=>16155, :name=>"C1"}, {:id=>16158, :name=>"C2"}] 
h.update(chapters: b) 
    #=> {:publisher=>{:id=>96, :name=>"P1"}, 
    # :subject=>{:id=>233, :name=>"S1"}, 
    # :volume=>{:id=>1136, :name=>"V1"}, 
    # :chapters=>[{:id=>16155, :name=>"C1"}, {:id=>16158, :name=>"C2"}] 
    # } 

Hash#merge könnte anstelle von Hash#update verwendet werden.

+0

Ich bin mir nicht sicher über Sie sagte 'Lassen arr gleich der Reihe von Hashes (das erste Array oben), können wir folgendes tun: Bedeutet es' arr = data'? –

+0

Wenn die Variable 'data' dieses Array enthält, ja. Wenn Sie ein Beispiel angeben, ist es hilfreich, jedem Eingabeobjekt eine Variable zuzuweisen (z. B. "data = [{: publisher => ...]"). Auf diese Weise können Leser auf diese Variable (hier nur "Daten") in Kommentaren und Antworten verweisen, ohne sie definieren zu müssen. Sie können Ihre Frage bearbeiten, um dies zu tun. –

+0

Ich habe den Variablennamen 'data = []' definiert und in meine Frage 'hash' eingefügt, meinst du das? –

1

Wie wäre:

data = {} 

    question_set.questions.each do |q| 
     key = "#{q.publisher.id}:#{q.book_volume.subject.id}:#{q.book_volume_id}" 
     if data[key].present? 
     data[key][:chapter] << {id: q.section_id, name: q.section.name} 
     else 
     data[key] = {publisher: {id: q.publisher.id, name: q.publisher.name}, subject: {id: q.book_volume.subject.id, name: q.book_volume.subject.name}, volume: {id: q.book_volume_id, name: q.book_volume.name}, chapter: [{id: q.section_id, name: q.section.name}]} 
     end 
    end 

    result = data.values 

Verwendung der Kombination von publisher'id, subject'id und volume'id als eindeutiger Schlüssel Ihre Daten zu kombinieren.

+0

Aber meine Daten sind ein Array-Typ, es würde viele Hash enthalten. –

+0

In der letzten Zeile, result = data.values, wird es zu einem Array. –

+0

Oh, ich vermisse es, es ist wirklich erstaunlich Ansatz! –