18

我有两个哈希:

hash1 = {1 => "a" , 2 => "b" , 3 => "c" , 4 => "d"} 
hash2 = {3 => "hello", 4 => "world" , 5 => "welcome"} 

我需要一个包含两个哈希中的公共键的哈希:

hash3 = {3 => "hello" , 4 => "world"}

是否可以在没有任何循环的情况下做到这一点?

4

4 回答 4

19
hash3 = hash1.keep_if { |k, v| hash2.key? k }

这不会与问题中的代码产生相同的效果,而是会返回:

hash3 #=> { 3 => "c", 4 => "d" }

哈希的顺序在这里很重要。这些值将始终取自#keep_if发送到的散列。

hash3 = hash2.keep_if { |k, v| hash1.key? k }
#=> {3 => "hello", 4 => "world"}
于 2013-07-12T06:55:23.017 回答
15

我会这样做:

hash1 = {1 => "a" , 2 => "b" , 3 => "c" , 4 => "d"} 
hash2 = {3 => "hello", 4 => "world" , 5 => "welcome"} 

Hash[(hash1.keys & hash2.keys).zip(hash2.values_at(*(hash1.keys & hash2.keys)))]
=> {3=>"hello", 4=>"world"}

可以减少一点:

keys = (hash1.keys & hash2.keys)
Hash[keys.zip(hash2.values_at(*keys))]

诀窍在于 Array 的&方法。文档说:

Set Intersection — 返回一个新数组,其中包含两个数组共有的元素,不包括任何重复项。顺序从原始数组中保留。


以下是一些基准来展示什么是最有效的方法:

require 'benchmark'

HASH1 = {1 => "a" , 2 => "b" , 3 => "c" , 4 => "d"} 
HASH2 = {3 => "hello", 4 => "world" , 5 => "welcome"} 

def tinman
  keys = (HASH1.keys & HASH2.keys)
  Hash[keys.zip(HASH2.values_at(*keys))]
end

def santhosh
  HASH2.select {|key, value| HASH1.has_key? key }
end
def santhosh_2
  HASH2.select {|key, value| HASH1[key] }
end

def priti
  HASH2.select{|k,v| HASH1.assoc(k) }
end

def koraktor
  HASH1.keep_if { |k, v| HASH2.key? k }
end
def koraktor2
  HASH2.keep_if { |k, v| HASH1.key? k }
end

N = 1_000_000
puts RUBY_VERSION
puts "N= #{N}"

puts [:tinman, :santhosh, :santhosh_2, :priti, :koraktor, :koraktor2].map{ |s| "#{s.to_s} = #{send(s)}" }
Benchmark.bm(11) do |x|
  x.report('tinman') { N.times { tinman() }}
  x.report('santhosh_2') { N.times { santhosh_2() }}
  x.report('santhosh') { N.times { santhosh() }}
  x.report('priti') { N.times { priti() }}
  x.report('koraktor') { N.times { koraktor() }}
  x.report('koraktor2') { N.times { koraktor2() }}
end

红宝石 1.9.3-p448:

1.9.3
N= 1000000
tinman = {3=>"hello", 4=>"world"}
santhosh = {3=>"hello", 4=>"world"}
santhosh_2 = {3=>"hello", 4=>"world"}
priti = {3=>"hello", 4=>"world"}
koraktor = {3=>"c", 4=>"d"}
koraktor2 = {3=>"hello", 4=>"world"}
                  user     system      total        real
tinman        2.430000   0.000000   2.430000 (  2.430030)
santhosh_2    1.000000   0.020000   1.020000 (  1.003635)
santhosh      1.090000   0.010000   1.100000 (  1.104067)
priti         1.350000   0.000000   1.350000 (  1.352476)
koraktor      0.490000   0.000000   0.490000 (  0.484686)
koraktor2     0.480000   0.000000   0.480000 (  0.483327)

在 Ruby 2.0.0-p247 下运行:

2.0.0
N= 1000000
tinman = {3=>"hello", 4=>"world"}
santhosh = {3=>"hello", 4=>"world"}
santhosh_2 = {3=>"hello", 4=>"world"}
priti = {3=>"hello", 4=>"world"}
koraktor = {3=>"c", 4=>"d"}
koraktor2 = {3=>"hello", 4=>"world"}
                  user     system      total        real
tinman        1.890000   0.000000   1.890000 (  1.882352)
santhosh_2    0.710000   0.010000   0.720000 (  0.735830)
santhosh      0.790000   0.020000   0.810000 (  0.807413)
priti         1.030000   0.010000   1.040000 (  1.030018)
koraktor      0.390000   0.000000   0.390000 (  0.389431)
koraktor2     0.390000   0.000000   0.390000 (  0.389072)

Koraktor 的原始代码不起作用,但他在第二次代码通过时很好地扭转了它,并以最快的速度离开了。我添加了该santhosh_2方法以查看删除key?会产生什么效果。它加快了例行程序,但还不足以赶上 Koraktor 的。


仅出于文档目的,我调整了 Koraktor 的第二个代码以删除该key?方法,并从中节省了更多时间。这是添加的方法和新的输出:

def koraktor3
  HASH2.keep_if { |k, v| HASH1[k] }
end

1.9.3
N= 1000000
tinman = {3=>"hello", 4=>"world"}
santhosh = {3=>"hello", 4=>"world"}
santhosh_2 = {3=>"hello", 4=>"world"}
priti = {3=>"hello", 4=>"world"}
koraktor = {3=>"c", 4=>"d"}
koraktor2 = {3=>"hello", 4=>"world"}
koraktor3 = {3=>"hello", 4=>"world"}
                  user     system      total        real
tinman        2.380000   0.000000   2.380000 (  2.382392)
santhosh_2    0.970000   0.020000   0.990000 (  0.976672)
santhosh      1.070000   0.010000   1.080000 (  1.078397)
priti         1.320000   0.000000   1.320000 (  1.318652)
koraktor      0.480000   0.000000   0.480000 (  0.488613)
koraktor2     0.490000   0.000000   0.490000 (  0.490099)
koraktor3     0.390000   0.000000   0.390000 (  0.389386)

2.0.0
N= 1000000
tinman = {3=>"hello", 4=>"world"}
santhosh = {3=>"hello", 4=>"world"}
santhosh_2 = {3=>"hello", 4=>"world"}
priti = {3=>"hello", 4=>"world"}
koraktor = {3=>"c", 4=>"d"}
koraktor2 = {3=>"hello", 4=>"world"}
koraktor3 = {3=>"hello", 4=>"world"}
                  user     system      total        real
tinman        1.840000   0.000000   1.840000 (  1.832491)
santhosh_2    0.720000   0.010000   0.730000 (  0.737737)
santhosh      0.780000   0.020000   0.800000 (  0.801619)
priti         1.040000   0.010000   1.050000 (  1.044588)
koraktor      0.390000   0.000000   0.390000 (  0.387265)
koraktor2     0.390000   0.000000   0.390000 (  0.388648)
koraktor3     0.320000   0.000000   0.320000 (  0.327859)
于 2013-07-12T07:00:35.673 回答
11
hash2.select {|key, value| hash1.has_key? key }
# => {3=>"hello", 4=>"world"}
于 2013-07-12T07:12:02.323 回答
10

Ruby 2.5 添加了Hash#slice,它允许像这样的紧凑代码:

hash3 = hash1.slice(*hash2.keys)

在较旧的 rubies 中,这在使用 active support 的哈希扩展的 rails 或项目中是可能的。

于 2018-05-26T09:07:03.837 回答