【问题标题】:Use value of key as part of value of another key in the same hash使用键的值作为同一哈希中另一个键的值的一部分
【发布时间】:2016-09-13 21:39:00
【问题描述】:

如何在 Ruby 中将一个键的值作为操作的一部分在同一个哈希中使用另一个键的值,在声明它的那一刻?

我的意思是这样的:

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

我认为这将很有用,例如,如果值 quantity 是由外部 API 分配的,其中消耗有限的查询,那么如果 total 也请求,则花费两个查询而不是询问“数量”只花费一个查询。

【问题讨论】:

  • 你想在初始化散列时基于其他键的一些值?

标签: ruby-on-rails ruby hash key


【解决方案1】:

我建议创建一个 Purchase 模型来进行计算:

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

将您的代码更改为:

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

作为奖励:此模型易于理解且易于测试。

【讨论】:

  • 我可以建议关键字参数而不是属性哈希吗?
  • 我也更喜欢 kwargs。但是要使用必需的关键字参数,您至少需要使用 Ruby 2.1,并且许多开发人员仍然出于各种原因使用旧版本。
  • @Phrogz 是什么意思?
  • @IvánCortésRomero 请参阅我答案的底部以获取类中的关键字参数,而不是依赖位置参数。位置(如在我的 Struct 示例中)导致输入更少,但代码清晰度和健壮性也更低。
【解决方案2】:

在哈希声明期间,您要求的内容是不可能的。您可以像这样更改代码:

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

或者只是最后一行:

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

或其他涉及稍微少一点打字的愚蠢方式:

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

但是,我建议在这种情况下,您不应将 "denormalized" data 存储在您的哈希中。由于total 取决于数量或价格,如果其中任何一个发生更改,您的哈希将无效。

您可以通过在您的哈希上创建一个特殊的default_proc 来解决这个问题,每次请求时都会即时计算总数:

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

但是,创建一个类或结构来执行此操作会更清楚。 Struct 对您来说代码更少:

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

但是,Struct (by default) 不允许关键字参数。通过编写自己的类,您可以按任意顺序提供参数,甚至提供默认值:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-04
    • 2011-10-03
    相关资源
    最近更新 更多