处理零值的优雅“Ruby方式”是什么?

时间:2014-07-16 19:50:03

标签: ruby

所以我想根据是否已经给出输入来有条件地分配变量。

例如

@name = params[:input]['name'] || "Name not yet given"

但是,如果尚未传递参数,则会出现错误

method [] does not exist for nil class

我有两个想法来解决这个问题。一个是将[]方法添加到nil类。类似的东西:

class NilClass
    def []
        self
    end
end

我的另一个想法是使用if语句

if params[:input].nil?
    @name = params[:input]['name']
else
    @name = "Name not yet given"
end

然而,这些解决方案都不是很正确。

什么是" ruby​​方式"?

7 个答案:

答案 0 :(得分:6)

一种方法是使用Hash#fetch

params[:input].to_h.fetch('name', "Name not yet given")

答案 1 :(得分:2)

@name = params[:input].nil? ? "Name not yet given" : params[:input]['name']
  • 使用当前的应用程序,可以选择排除.nil?

另请参阅我的递归解决方案:https://stackoverflow.com/a/24588976/445221

答案 2 :(得分:2)

您总是可以编写一些代码来使其他代码变甜。

class Hash
  def deep_fetch(*path)
    path.reduce(self) { |memo, elem| memo ? memo[elem] : nil }
  end
end

params = { input: { name: 'sergio' } }

params.deep_fetch(:input, :name) # => "sergio"
params.deep_fetch(:omg, :lol, :wtf) # => nil

答案 3 :(得分:1)

我喜欢使用NullObjects,或者更具体地说,黑洞对象用于此类事情。 Avdi Grimm为这个名为naught的结构赋予了我们巨大的红宝石宝石。因此,对于您的情况,我将安装gem,然后从创建项目特定的Null对象开始:

# add this to a lib file such as `lib/null_object.rb`
require 'naught'

NullObject = Naught.build do |config|
  config.define_explicit_conversions
  config.define_implicit_conversions
  config.black_hole

  if $DEBUG
    config.traceable
  else
    config.singleton
  end
end

然后,在需要的地方加入NullObject::Conversions并自信地前往城镇!

# my_class.rb
require 'null_object.rb'
include NullObject::Conversions

Maybe(params[:input])["name"].to_s.presence || "Name not yet given"
# => "Name not yet given"

这种黑洞方法的好处在于,任何额外的链接都不需要额外的步骤。你可以简单地将方法链接在一起,只要你愿意(自信)假设结果很好。然后,最后将值转换为期望的类型,如果链中的某些内容在您预期之前返回nil,那么显式转换将为您提供该版本的基本版本。

Maybe(params[:thing1])[:thing2][:thing3].map(&:to_i).sum.to_i
# => 0

或者,如果您愿意,可以使用Actual将黑洞对象转换回实际值:

Actual(Maybe(params[:input])["name"]) || "Name not yet given"

有关Null对象模式的更多信息,请查看Avdi Grimm's post on the subject。总而言之,这是获得信心并停止类型检查的好方法(记住,甚至像nil一样检查.try()是类型检查!)。鸭子打字应该让我们免于类型检查!

答案 4 :(得分:1)

据我了解,对于散列h,您想知道是否

  • h有一个键:input,如果有的话
  • h[:input]是一个哈希,如果是的话
  • h[:input]有一个键"name"

如果对所有三个人都是“是”,请返回h[:input]["name"];否则返回"Name not yet given"

所以请写下来:

def get_name(h)
  if (h[:input].is_a? Hash) && h[:input].key?("name") 
    h[:input]["name"]
  else
    "Name not yet given"
  end
end

params = { hat: "cat" }
get_name(params)
  #=> "Name not yet given"

params = { input: "cat" }
get_name(params)
  #=> "Name not yet given"

params =  { input: {} }
get_name(params)
  #=> "Name not yet given"

params =  { input: { "moniker"=>"Jake" } }
get_name(params)
  #=> "Name not yet given"

params =  { input: { "name"=>"cat" } }
get_name(params)
  #=> "cat"

另一种方式:

def get_name(h)
  begin
    v = h[:input]["name"]
    v ? v : "Name not yet given"
  rescue NoMethodError
    "Name not yet given"
  end
end

答案 5 :(得分:1)

您可以使用Ruby 2.3.0中引入的Hash#dig

@name = params.dig(:input, 'name') || "Name not yet given"

同样,如果你想在链接方法时优雅地处理nil返回,你可以使用Ruby 2.3.0中引入的safe navigation operator

@name = object&.input&.name || "Name not yet given"

答案 6 :(得分:0)

尝试获取密钥:

params[:input].try(:fetch, 'name', "Name not yet given")

假设你很可能在轨道上,否则你可以连接fetchs:

params.fetch(:input, {}).fetch 'name', "Name not yet given"

定义这样的参数是一种常见的做法:

def input_params
  params.fetch(:input, {})
end

这将问题减少到:

input_params[:name] || 'Whatever'