微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ruby – 为特定值切片params哈希

概要

给定哈希,基于要使用的密钥列表创建子集哈希最有效的方法是什么?

h1 = { a:1,b:2,c:3 }        # Given a hash...
p foo( h1,:a,:c,:d )       # ...create a method that...
#=> { :a=>1,:c=>3,:d=>nil } # ...returns specified keys...
#=> { :a=>1,:c=>3 }          # ...or perhaps only keys that exist

细节

Sequel数据库工具包允许通过传入Hash来创建或更新模型实例:

foo = Product.create( hash_of_column_values )
foo.update( another_hash )

Sinatra Web框架提供了一个名为params的Hash,包括表单变量,查询字符串参数和路由匹配.

如果我创建一个表单,只保留与数据库列相同的字段,并将其发布到此路由,一切工作都非常方便:

post "/create_product" do
  new_product = Product.create params
  redirect "/product/#{new_product.id}"
end

但是,这既脆弱又危险.这是危险的,因为恶意黑客可以发布一个不打算更改的列,并更新它们的表单.它很脆弱,因为使用与此路线相同的形式将无法正常工作:

post "/update_product/:foo" do |prod_id|
  if product = Product[prod_id]
    product.update(params)
    #=> <Sequel::Error: method foo= doesn't exist or access is restricted to it>
  end
end

所以,为了稳健性和安全性,我希望能够写出来:

post "/update_product/:foo" do |prod_id|
  if product = Product[prod_id]
    # Only update two specific fields
    product.update(params.slice(:name,:description))
    # The above assumes a Hash (or Sinatra params) monkeypatch
    # I will also accept standalone helper methods that perform the same
  end
end

…而不是更冗长和非DRY选项:

post "/update_product/:foo" do |prod_id|
  if product = Product[prod_id]
    # Only update two specific fields
    product.update({
      name:params[:name],description:params[:description]
    })
  end
end

更新:基准

以下是(当前)实现的基准测试结果:

user     system      total        real
sawa2           0.250000   0.000000   0.250000 (  0.269027)
phrogz2         0.280000   0.000000   0.280000 (  0.275027)
sawa1           0.297000   0.000000   0.297000 (  0.293029)
phrogz3         0.296000   0.000000   0.296000 (  0.307031)
phrogz1         0.328000   0.000000   0.328000 (  0.319032)
activesupport   0.639000   0.000000   0.639000 (  0.657066)
mladen          1.716000   0.000000   1.716000 (  1.725172)

@sawa的第二个答案是最快的,在我的基于Tap的实现之前的头发(基于他的第一个答案).选择添加对has_key的检查?增加了很少的时间,并且仍然是ActiveSupport的两倍以上.

以下是基准代码

h1 = Hash[ ('a'..'z').zip(1..26) ]
keys = %w[a z c d g A x]
n = 60000

require 'benchmark'
Benchmark.bmbm do |x|
  %w[ sawa2 phrogz2 sawa1 phrogz3 phrogz1 activesupport mladen ].each do |m|
    x.report(m){ n.times{ h1.send(m,*keys) } }
  end
end

解决方法

我改变了头脑.前一个似乎没有什么好处.
class Hash
  def slice1(*keys)
    keys.each_with_object({}){|k,h| h[k] = self[k]}
  end
  def slice2(*keys)
    h = {}
    keys.each{|k| h[k] = self[k]}
    h
  end
end

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐