2017-06-13 4 views
4

Ich versuche, ein Array/Hash aus einem Array von mehreren Hashes mit gleichen Schlüsseln und einem Durchschnitt von Werten zu erstellen. Meine Array:Hinzufügen von Hashes aus einem Array

[{:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"}, {:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"}] 

Jetzt möchte ich etwas wie folgt zurück:

{:amount=>952000, :gross_amount=>952000, :tax=>152000, :hotel_fees=>0, :base_fare=>800000, :currency=>"INR"} 

wo Werte der Durchschnitt der Werte von jedem Hash mit dem gleichen Schlüssel sind.

Gibt es eine einfache Möglichkeit, dies zu tun. Ich habe versucht, Merge zu verwenden, aber Währung wird 0 damit.

Mein Versuch:

p[0].merge(p[1]){|k,v1,v2| (v1+v2)/2 unless v1 && v2 == "INR"} 

Edit:

Eigentlich mein Problem noch nicht zu Ende hier, also nach dem Durchschnitt bekommt ich die Werte in einem anderen Hash einzufügen benötigt. So habe ich so etwas wie diese:

 price_array = offer_values.map do |v| 
      v.inject do |k, v| 
      k.merge!(price: k[:price].merge(v[:price]){|_, a, b| [a, b].flatten }) 
      end 
     end 
     price_array.map do |o| 
      o[:price] = {}.tap{ |h| o[:price].each {|k, list| h[k] = list.all?{|e| [Fixnum, NilClass].include? e.class} ? list.map(&:to_i).sum/list.size : list.compact.first ; h } } 
     end 

Wo offer_array in separatem Hashes das mit meiner ursprünglichen/ersten Array ist. Dies habe ich mit 2 und 3 Hashes versucht und es funktioniert.

Wenn Sie irgendwelche Vorschläge zur Verbesserung des Codes haben, ist es offen.

+1

_ "Ich habe versucht, Merge" _ - könnten Sie Ihren Versuch zeigen? – Stefan

+0

Zusammenführen gibt '' zurück: {: Menge => 952000,: Bruttosumme => 952000,: Steuer => 152000,: Hotel_Zahle => 0,: Basis_Farbe => 800000,: Währung => Keine} ' – Ravi

+0

Ich kann nicht haben Währung Null. – Ravi

Antwort

1

Für zwei Hashes in Array könnten Sie inject und merge prüft wird, ob der Wert für die beiden currency Tasten Fixnum Klasse sind, wenn nicht, dann wird der Wert der Währung nehmen "INR" im ersten Hash und verwenden Sie es:

array = [ 
    {:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"}, 
    {:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"} 
] 

p array.inject{|k,v| k.merge(v){|_,a,b| [a,b].all?{|e| e.is_a?(Fixnum)} ? (a+b)/2 : b}} 
# => {:amount=>952000, :gross_amount=>952000, :tax=>152000, :hotel_fees=>0, :base_fare=>800000, :currency=>"INR"} 

Für zwei oder mehr Hashes in einem Array können Sie versuchen, mit:

main_array = [ 
    {:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"}, 
    {:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"}, 
    {:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"}, 
] 
array_result = main_array.flat_map(&:to_a).group_by(&:first).map do |key, array| 
    { 
    key => (
     result = array.inject(0) do |total, (_, value)| 
     value.is_a?(Fixnum) ? total + value : value 
     end 
     result.is_a?(Fixnum) ? result/main_array.size : result 
    ) 
    } 
end 
p array_result 
+0

Was ist '_' in' | _, a, b | 'tut? Kannst du bitte Erklären? – Ravi

+0

@Ravi es ist in meiner Antwort erklärt, erste Attribut ist Schlüssel, er nannte es _ zweites Attribut ist der alte Wert oder Wert aus dem ersten Hash, dritte Attribut ist der neue Wert oder Wert aus dem zweiten Hash –

+0

_ hier ist nur ein Parameter, er nannte es einen zufälligen Namen, weil er nicht verwenden wird ... –

3

Irb

2.2.3 :011 > b = {test1: 30, test2: 40} 
=> {:test1=>30, :test2=>40} 
2.2.3 :012 > a = {test1: 20, test2: 60} 
=> {:test1=>20, :test2=>60} 
2.2.3 :013 > c = a.merge(b){|key, oldval, newval| (newval + oldval)/2} 
=> {:test1=>25, :test2=>50} 
+0

ohh ich dachte es wäre etwas anderes. – Ravi

2

Die angenommene Antwort funktioniert nicht für mehr als 2 Hashes, da merge nur 2 mal 2 funktioniert und Sie hier den Durchschnitt berechnen.

(((3 + 2)/2) + 2.5)/2 is different from (3 + 2 + 2.5)/3 

Also schrieb ich ein Stück Code, das tun konnte, was Sie wollen, was Größe des Arrays Sie haben

def self.merge_all_and_average(array) 
    new_hash = {} 
    unless array.empty? 
     array[0].keys.each do |key| 
     if array[0][key].class == Fixnum 
      total = array.map { |i| i[key] }.inject(0) { |sum, x| sum + x } 
      new_hash = new_hash.merge(key => total/array.size) 
     else 
      new_hash = new_hash.merge(key => array[0][key]) 
     end 
     end 
    end 
    new_hash 
    end 
+0

Ich weiß, dass es nicht für mehr als zwei Hash funktioniert. Ich wollte nur eine Idee, wie ich damit umgehen könnte. Ich war auf dem richtigen Weg, konnte aber keine richtige Antwort bekommen. Mein Problem war nie ganz anders und verschiedene andere Dinge waren beteiligt. Das war nur ein Teil davon, wo ich verwirrt wurde. Die Lösung, die ich verwendet habe, ist Ihrer ähnlich. Überprüfen Sie meine Bearbeitung. – Ravi

2

Dies sollte mit einer beliebigen Anzahl von Hashes arbeiten:

data = [ 
    { amount: 897_500, gross_amount: 897_500, tax: 147_500, hotel_fees: 0, base_fare: 750_000, currency: 'INR' }, 
    { amount: 1_006_500, gross_amount: 1_006_500, tax: 156_500, hotel_fees: 0, base_fare: 850_000, currency: 'INR' }, 
    { amount: 1_006_500, gross_amount: 1_006_500, tax: 156_500, hotel_fees: 0, base_fare: 850_000, currency: 'INR' } 
] 

transposed_hashes = data.each_with_object(Hash.new{|h, k| h[k] = []}) do |h, mem| 
    h.each do |k, v| 
    mem[k] << v 
    end 
end 
# {:amount=>[897500, 1006500, 1006500], :gross_amount=>[897500, 1006500, 1006500], :tax=>[147500, 156500, 156500], :hotel_fees=>[0, 0, 0], :base_fare=>[750000, 850000, 850000], :currency=>["INR", "INR", "INR"]} 

average_hash = transposed_hashes.map do |k, v| 
    new_value = if v[0].is_a? Integer 
       v.sum.to_f/v.size 
       else 
       v[0] 
       end 
    [k, new_value] 
end.to_h 

puts average_hash 
# {:amount=>970166.6666666666, :gross_amount=>970166.6666666666, :tax=>153500.0, :hotel_fees=>0.0, :base_fare=>816666.6666666666, :currency=>"INR"} 
+0

Ich mache auch etwas ähnliches, aber diese Methode erhöht meine Komplexität des Codes. – Ravi

Verwandte Themen