Ruby Ruby 里的展开操作符

wootaw · 2019年09月20日 · 最后由 zhengpd 回复于 2019年09月21日 · 5257 次阅读

ES6 中 Hash 有一个展开操作符 (spread operator) (...) 功能强大,在 ruby 中也有一个类似功能的操作符 (**)

用在函数参数中

def fun1(**options)
  puts "options #{options}"
end

fun1(one: 1, two: 2) # 输出 options {:one=>1, :two=>2}
fun1({one: 1, two: 2}) # 输出 options {:one=>1, :two=>2}
def fun2(one:, two:, **options)
  puts "options #{options}"
end

fun2(one: 1, two: 2) # 输出 options {}
fun2(one: 1, two: 2, three: 3) # 输出 options {:three=>3}
fun2(one: 1, two: 2, { three: 3 }) # 输出 SyntaxError: unexpected ')', expecting =>

用在 Hash 的合并中

合并两个 Hash:

a = { one: 1, two: 2 }
b = { two: 22, three: 3 }
{ **a, **b } # = {:one=>1, :two=>22, :three=>3}

还可以这么写:

b = { two: 22, three: 3 }
{ one: 1, two: 2, **b } # = {:one=>1, :two=>22, :three=>3}

看看基准测试

require 'benchmark'
n = 5000000
def merge
  hash = { one: 1 }
  { two: 2 }.merge(hash)
end

def spread
  hash = { one: 1 }
  { two: 2, **hash }
end

Benchmark.bm(2) do |x|
  x.report('merge: ') { n.times { merge } }
  x.report('spread:') { n.times { spread } }
end

结果:(ruby 2.6.2)(**) 操作符会比 merge 稍稍快一点点

         user     system      total        real
merge:   1.640279   0.014161   1.654440 (  1.800680)
spread:  1.460447   0.010523   1.470970 (  1.550659)

需要注意的几个地方

(**) 操作符只能用在 key 为 symbol 的 Hash

c = { "two" => 22, "three" => 33 }
{ one: 1, two: 2, **c } # TypeError: wrong argument type String (expected Symbol)

另外,在 Rails 中的 controller 里

def fun3(**options); end

object_params = params.permit(:one, :two)
fun3(object_params) # 这样用是不行的 ActionController::UnfilteredParameters - unable to convert unpermitted parameters to hash:

因为 params 不是一个 Hash

fun3(object_params.to_h.symbolize_keys)

这样就可以了

ruby ** 叫 double splat operator,跟 js 名称不同

需要 登录 后方可回复, 如果你还没有账号请 注册新账号