概要
给定哈希,基于要使用的密钥列表创建子集哈希最有效的方法是什么?
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