提高性能以从 Ruby 中的哈希数组中查找 ID 数组

Improve performance to find an array of ids from array of hashes in Ruby

考虑一个哈希数组

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

考虑一个 characters/numbers/ids

的数组
b=['1','2','5']

我想将 b 的 ID 与 a 匹配。对于所有匹配项,我想用相应的哈希替换 b 的值。

在上面的例子中,值'1'和'2'在a和b之间是公共的,所以我将b中的'1'和'2'替换为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
}

有更好的解决办法吗?一个更圆滑的。我是 ruby.

的新手

你可以使用Enumerable#map, with Enumerable#select

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

这将提高速度:

#!/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)

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

我假设 a:id 的值是唯一的。

首先构造一个查找散列:

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 较大时,查找哈希的使用特别有效。