【问题标题】:Passing `nil` to method using default named parameters使用默认命名参数将`nil`传递给方法
【发布时间】:2016-06-14 08:22:34
【问题描述】:

在 Rails 项目中,我正在收集一个包含 10-15 个键值对的哈希,并将其传递给一个类(服务对象)以进行实例化。对象属性应该从散列中的值设置,除非没有值(或nil)。在这种情况下,最好将该属性设置为默认值。

我不想在创建对象之前检查哈希中的每个值是否不是nil,而是想找到一种更有效的方法。

我正在尝试使用具有默认值的命名参数。我不知道这是否有意义,但我想在使用nil 调用参数时使用默认值。我为此功能创建了一个测试:

class Taco
  def initialize(meat: "steak", cheese: true, salsa: "spicy")
    @meat = meat
    @cheese = cheese
    @salsa = salsa
  end
  def assemble
    "taco with: #@meat + #@cheese + #@salsa"
  end
end

options1 = {:meat => "chicken", :cheese => false, :salsa => "mild"}
chickenTaco = Taco.new(options1)
puts chickenTaco.assemble
# => taco with: chicken + false + mild

options2 = {}
defaultTaco = Taco.new(options2)
puts defaultTaco.assemble
# => taco with: steak + true + spicy

options3 = {:meat => "pork", :cheese => nil, :salsa => nil}
invalidTaco = Taco.new(options3)
puts invalidTaco.assemble
# expected => taco with: pork + true + spicy
# actual => taco with: pork +  +

【问题讨论】:

  • 感谢sawa的格式化帮助

标签: ruby named-parameters


【解决方案1】:

如果你想遵循面向对象的方法,你可以将你的默认设置隔离在一个单独的方法中,然后使用Hash#merge

class Taco
  def initialize (args)
    args = defaults.merge(args)
    @meat   = args[:meat]
    @cheese = args[:cheese]
    @salsa  = args[:salsa]
  end

  def assemble
     "taco with: #{@meat} + #{@cheese} + #{@salsa}"
  end

  def defaults
   {meat: 'steak', cheese: true, salsa: 'spicy'}
  end  
end

然后按照@sawa 的建议(谢谢),使用 Rails 的Hash#compact 输入哈希值,明确定义了nil 值,您将得到以下输出:

taco with: chicken + false + mild
taco with: steak + true + spicy
taco with: pork + true + spicy

编辑:

如果你不想使用 Rails 的精彩 Hash#compact 方法,可以使用 Ruby 的 Array#compact 方法。将initialize 方法中的第一行替换为:

args = defaults.merge(args.map{|k, v| [k,v] if v != nil }.compact.to_h)

【讨论】:

  • Hash#compact 之外使用Hash#merge 是确保我的对象获得默认值而不是nil 属性值的好方法。非常感谢!
【解决方案2】:

一旦您使用命名参数传递了一个值,该方法调用就无法访问该参数的默认值。

您要么必须 (i) 分配默认值,而不是在方法配置文件中,而是在方法主体中,如 sagarpandya82 的答案,或者 (ii) 在使用 Rails 将参数传递给这样的方法之前删除 nil 值'Hash#compact:

options3 = {:meat => "pork", :cheese => nil, :salsa => nil}
invalidTaco = Taco.new(options3.compact)

【讨论】:

  • 我删除了我的答案,因为我无法获得 OP 所需的输出。如果您知道如何解决,请随时将其纳入您的答案。
  • @sagarpandya82 我看不出你的回答有什么问题。至少,它比接受的答案要好得多,后者甚至不考虑默认值(除了对 OP 评论的事后回应)。
  • OP 想要taco with: chicken + false + mild 作为第一个字符串。我的代码打印taco with: chicken + true + mild。这是唯一的原因。你能找到解决办法吗?
  • 谢谢@sawa,但我认为在这种情况下使用|| 是有缺陷的,所以我提交了一个包含您的答案的新答案。
  • @sawa 我真的很喜欢 Hash#compact 方法,并且在这种情况下使用它。感谢您进一步了解 Ruby 语言!
【解决方案3】:

我认为关键字参数不适合您的情况。似乎哈希更合适。

class Taco
    attr_accessor :ingredients

    def initialize(ingredients = {})
        @ingredients = ingredients
    end

    def assemble
        "taco with: #{ingredients[:meat]} + #{ingredients[:cheese]} + #{ingredients[:salsa]}"
    end
end

您甚至可以缩短 assemble 方法来列出所有成分

def assemble
    string = "taco with: " + ingredients.values.join(" + ")
end

它会像你期望的那样工作

options1 = {:meat => "chicken", :cheese => false, :salsa => "mild"}
chicken_taco = Taco.new(options1)
puts chicken_taco.assemble() # output: taco with: chicken + false + mild

值得一提的是,Ruby 更喜欢 chicken_tacos 而不是 chickenTacos

【讨论】:

  • 感谢您的回答!有什么方法可以为您在回答中提到的某些 ingredients 哈希键提供默认值吗?例如,如果meat 没有作为成分给出,它仍然会打印taco with: {default_meat} + {other_ingredient_a} + ...?
  • ingredients[:meat] ||= "default meat" 将允许您有条件地为该键分配默认值(如果没有给出)。
猜你喜欢
  • 2012-10-22
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-07
  • 2011-11-11
  • 1970-01-01
相关资源
最近更新 更多