2016-05-17 12 views
1

Wie verwenden Sie den Wert eines Schlüssels als Teil der Operation in Wert eines anderen Schlüssels in demselben Hash in Ruby, um es zu deklarieren?Verwenden Sie Wert des Schlüssels als Teil des Werts eines anderen Schlüssels in demselben Hash

Ich meine etwas wie folgt aus:

purchase = {product: 'phone', 
      quantity: 5, 
      price: 120, 
      total: self.quantity * self.price 
      } 

Ich denke, das wäre nützlich, zum Beispiel, wenn der Wert quantity von einem externen API zugeordnet ist, wo aus begrenzten Anfragen verbraucht, so dass, wenn total Anfragen auch eine Gibt zwei Abfragen aus, anstatt nach "Menge" zu fragen und nur eine Abfrage auszugeben.

+0

möchten Sie beim Initialisieren Hash seine einige Werte basierend auf anderen Schlüsseln? – Thorin

+0

ja, das ist es. –

Antwort

1

Was Sie fordern, ist während der Hash-Deklaration nicht möglich. Sie können Ihren Code wie folgt ändern:

purchase = {} 
purchase[:product] = 'phone' 
purchase[:quantity] = 5 
purchase[:price] = 120 
purchase[:total] = purchase[:quantity] * purchase[:price] 

Oder einfach nur die letzte Zeile:

purchase = { 
product: 'phone', 
quantity: 5, 
price: 120 
} 
purchase[:total] = purchase[:quantity] * purchase[:price] 

oder andere dumme Art und Weise, die etwas weniger Tipp beinhalten:

purchase = { 
product: 'phone', 
quantity: 5, 
price: 120 
}.tap{ |h| h[:total] = h[:quantity] * h[:price] } 

Allerdings würde ich vorschlagen In einem Fall wie diesem sollten Sie "denormalized" data NICHT in Ihrem Hash speichern. Da total von der Menge oder dem Preis abhängig ist, wird der Hash ungültig, wenn sich einer der beiden ändert.

Sie können dieses Problem umgehen, indem Sie eine spezielle default_proc auf Hash erstellen, die im laufenden Betrieb jedes Mal die Gesamt berechnet sie angefordert wird:

purchase = { 
product: 'phone', 
quantity: 5, 
price: 120 
} 
purchase.default_proc = ->(h,k){ 
    h[:quantity]*h[:price] if k==:total 
} 
p purchase[:total]  #=> 600 
purchase[:quantity] = 7 
p purchase[:total]  #=> 840 

Allerdings wäre es klar sein, eine Klasse oder Struktur zu erstellen um dies zu tun. Ein Struct ist weniger Code für Sie:

Purchase = Struct.new(:product,:quantity,:price) do 
    def total 
    quantity * price 
    end 
end 
purchase = Purchase.new('phone',5,120) 
p purchase.total      #=> 600 
purchase.quantity = 3 
p purchase.total      #=> 360 

jedoch die Struct nicht (by default) Keyword-Argumente ermöglichen. Durch Ihre eigene Klasse schreiben, können Sie die Argumente in beliebiger Reihenfolge, liefern und sogar einen Standardwert bieten:

class Purchase 
    attr_reader :product, :price, :quantity 
    def initialize(product:,price:,quantity:1) # default quantity 
    @product, @price, @quantity = product, price, quantity 
    end 
    def total 
    price*quantity 
    end 
end 

Purchase.new(price:10, product:'shoes').total    #=> 10 
Purchase.new(product:'shoes', price:10).total    #=> 10 
Purchase.new(quantity:3, price:10, product:'shoes').total #=> 30 
4

Ich würde vorschlagen, ein Purchase Modell zu erstellen, die in die Berechnung:

class Purchase 
    attr_reader :product, quantity, price 

    def initialize(attributes = {}) 
    @product = attributes[:product] 
    @quantity = attributes[:quantity] 
    @price = attributes[:price] 
    end 

    def total 
    quantity * price 
    end 

    def to_h 
    { 
     product: product, 
     quantity: quantity, 
     price: price, 
     total: total 
    } 
    end 
end 

als der Wandel Ihr Code zu:

purchase = Purchase.new(product: 'phone', quantity: 5, price: 120).to_h 

Als Bonus: Dieses Modell ist leicht zu verstehen und einfach zu testen.

+0

Kann ich Schlüsselwortargumente anstelle des Attributs hash vorschlagen? – Phrogz

+0

Ich würde auch Kwargs bevorzugen. Um jedoch die benötigten Schlüsselwortargumente zu verwenden, müssen Sie mindestens Ruby 2.1 verwenden und viele Entwickler verwenden aus verschiedenen Gründen immer noch ältere Versionen. – spickermann

+0

was meinst du @Phrogz? –