提高性能,从Ruby中的哈希数组中查找id数组

时间:2015-03-05 05:14:34

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-4

考虑一系列哈希

a=[{'id'=>'1','imageUrl'=>'abc'},{'id'=>'2','imageUrl'=>'efg'},{'id'=>'3','imageUrl'=>'hij'}]

考虑一组字符/数字/ ids

b=['1','2','5']

我想将b的id与a匹配。对于所有匹配,我想用相应的散列替换b的值。

在上面的示例中,值' 1'和' 2'在a和b之间很常见,所以我更换了' 1'和' 2'在b中具有相应的散列值a。

所以结果b成为

b=[[{"id"=>"1", "imageUrl"=>"abc"}], [{"id"=>"2", "imageUrl"=>"efg"}], []]

我写了以下代码:

b.each_with_index{|r,index|
puts index
k=a.select {|z| z["id"]==r }
b[index]=k
}

有更好的解决方案吗?更圆滑的一个。我是红宝石的新手。

3 个答案:

答案 0 :(得分:4)

您可以使用Enumerable#map的破坏性版本,Enumerable#select

b.map! {|id| a.select {|h| h['id'] == id }}
# => [[{"id"=>"1", "imageUrl"=>"abc"}], [{"id"=>"2", "imageUrl"=>"efg"}], []] 

答案 1 :(得分:2)

这将提高速度:

#!/usr/bin/env ruby
require 'pp'
require 'benchmark'

a = []
5000.times {|c| a << {"id" => "#{c}", "imageUrl" => "test#{c}"}}
b1 = (1..2500).to_a.shuffle.map(&:to_s) 
b2 = b1.dup()

puts "method1"
puts Benchmark.measure { b1.map! {|id| a.select {|h| h['id'] == id }} }

puts "method2"
result = Benchmark.measure do
    ah = Hash.new([])
    a.each{|x| ah[x["id"]] = x}
    b2.map!{|be| ah[be]}
end
puts result

结果:

method1
  2.820000   0.010000   2.830000 (  2.827695)
method2
  0.000000   0.000000   0.000000 (  0.002607)

更新基准 - 它使用b中的 250000 元素而不是 2500 (方法1注释掉以保护无辜者 - 它太慢而且我感到无聊等待它):

#!/usr/bin/env ruby
require 'pp'
require 'benchmark'

a = []
5000.times {|c| a << {"id" => "#{c}", "imageUrl" => "test#{c}"}}
b1 = (1..250000).to_a.collect{|x| x%2500}.shuffle.map(&:to_s)
b2 = b1.dup()
b3 = b1.dup()

# puts "method1"
# puts Benchmark.measure { b1.map! {|id| a.select {|h| h['id'] == id }} }

puts "method2"
result = Benchmark.measure do
    ah = Hash.new([])
    a.each{|x| ah[x["id"]] = x}
    b2.map!{|be| ah[be]}
end
puts result

puts "method3"
result = Benchmark.measure do
    h = a.each_with_object({}) { |g,h| h.update(g['id']=>g) }
    b3.map! { |s| h.key?(s) ? [h[s]] : [] }
end
puts result

结果是:

method2
  0.050000   0.000000   0.050000 (  0.045294)
method3
  0.100000   0.010000   0.110000 (  0.109646)

答案 2 :(得分:0)

[编辑:发布后我注意到@Mircea已经发布了相同的解决方案。我会在提及values_at替代方案时离开我。]

我认为:ida的值是唯一的。

首先构造一个查找哈希:

h = a.each_with_object({}) { |g,h| h.update(g['id']=>g) }
  #=> {"1"=>{"id"=>"1", "imageUrl"=>"abc"},
  #    "2"=>{"id"=>"2", "imageUrl"=>"efg"},
  #    "3"=>{"id"=>"3", "imageUrl"=>"hij"}} 

然后简单地遍历b,构建所需的数组:

b.map { |s| h.key?(s) ? [h[s]] : [] }
  #=> [[{"id"=>"1", "imageUrl"=>"abc"}],
  #    [{"id"=>"2", "imageUrl"=>"efg"}],
  #    []] 

可替换地,

arr = h.values_at(*b)
 #=> [{"id"=>"1", "imageUrl"=>"abc"},
 #    {"id"=>"2", "imageUrl"=>"efg"},
 #    nil]

然后:

arr.map { |e| e.nil? ? [] : [e] }
  #=> [[{"id"=>"1", "imageUrl"=>"abc"}],
  #    [{"id"=>"2", "imageUrl"=>"efg"}],
  #    []] 

您可以考虑使用arr进行后续计算,因为所需解决方案中的所有数组最多只包含一个元素。

b相对于a较大时,使用查找哈希特别有效。