2014-04-17 88 views
11

如何判断Ruby散列是否是另一个散列的子集(或包含)?红宝石哈希检查是子集?

例如:

hash = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7} 
hash.include_hash?({})   # true 
hash.include_hash?({f: 6, c: 3}) # true 
hash.include_hash?({f: 6, c: 1}) # false 
+3

马特,你似乎已经被赋予与(HTTP的[富人的尴尬]://dictionary.cambridge。组织/字典/英国/ AN-尴尬-的-财富)。 (链接英语为第二语言的人)。在SO上获得四个四分之一的质量答案是非常罕见的。 –

回答

9

由于红宝石2.3还可以执行以下操作来检查,如果这是一个子集

hash = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7} 
{} <= hash   # true 
{f: 6, c: 3} <= hash # true 
{f: 6, c: 1} <= hash # false 
+0

这是否与嵌套哈希工作?否则,这绝对是一个简洁的答案。 – nus

+0

@nus它确实与嵌套散列一起工作。但是,嵌套散列必须等于另一个,所以它不能只是一个子集: '{a:{x:1,y:2}} <= {a:{x:1,y:2}, b:3}#true' '{a:{x:1}} <= {a:{x:1,y:2},b:3}#false' – Pieter

+0

也可以用其他方法:'hash > = {f:6,c:3}' –

16

是我脑子里浮现使用Hash#merge方法解决办法:

class Hash 
    def include_hash?(hash) 
    merge(hash) == self 
    end 
end 

hash = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7} 
hash.include_hash?({}) 
# => true 
hash.include_hash?(f: 6, c:3) 
# => true 
hash.include_hash?(f: 6, c:1) 
# => false 
+0

我喜欢这个,马雷克,部分原因是因为它读得很好。 –

+2

而对于嵌套散列,您可以使用'active support' [deep_merge](http://apidock.com/rails/Hash/deep_merge) – Musaffa

+0

这非常优雅。我称之为“超集?”,并且我也制作了“子集”。 – nus

7

你可以在哈希转换为sets,也比使用方法subset?superset?进行检查(或它们各自的别名<=>=):

require 'set' 

hash.to_set.superset?({}.to_set) 
# => true 

hash.to_set >= {a: 1}.to_set 
# => true 

{a: 2}.to_set <= hash.to_set 
# => false 

更新:所提出的解决方案的标杆:

require 'fruity' 
require 'set' 

hash = ('aa'..'zz').zip('aa'..'zz').to_h 
# {"aa"=>"aa", "ab"=>"ab", ... 
find = ('aa'..'zz').zip('aa'..'zz').select { |k, _| k[0] == k[1] }.to_h 
# {"aa"=>"aa", "bb"=>"bb", ... 

compare(
    toro2k:  -> { hash.to_set >= find.to_set }, 
    MarekLipka: -> { hash.merge(find) == hash }, 
    CarySwoveland: -> { (find.to_a - hash.to_a).empty? }, 
    ArupRakshit: -> { arr = hash.to_a; find.all? { |pair| arr.include?(pair) } } 
) 

结果:

Running each test 2 times. Test will take about 1 second. 
MarekLipka is faster than toro2k by 3x ± 0.1 
toro2k is faster than CarySwoveland by 39.99999999999999% ± 10.0% 
CarySwoveland is faster than ArupRakshit by 1.9x ± 0.1 
+0

这很酷:-) –

+0

谢谢!基于'Set#superset?',我应该调用方法'Hash#superset?'而不是'Hash#include_hash?'。 – ma11hew28

+0

可能是'superset?'会是一个更好的名字,但我认为这只是一个品味问题。 – toro2k

6

你可以这样做:

def include_hash?(hash,subset_hash) 
    arr = hash.to_a 
    subset_hash.all? { |pair| arr.include?(pair) } 
end 
+0

任何人都可以检查'subset_hash.all? {| pair | arr.include?(pair)}'也在工作或不在...我远离电脑,所以无法测试相同的结果。 –

+0

我检查了一些测试用例,它似乎能够正常工作。 – ma11hew28

+0

@MattDiPasquale谢谢...我现在已经更新了它.. –

8

阵列的区别似乎最简单的:

class Hash 
    def include_hash?(h) 
    (h.to_a - to_a).empty? 
    end 
end 

h = {a: 1, b: 2} 
h.include_hash?({b: 2}) #=> true 
h.include_hash?({b: 3}) #=> false 
+0

我明白'(h.to_a - to_a)'..但是为了更好的可读性,...(h.to_a - self.to_a)'...... IMO –

+3

@Arup,我尊敬地不同意,但我知道你的观点被许多人共享其他。 Imo,'self.to_a'表明这里需要'self'(就像它后面跟着一个访问器或'class'一样)。我认为最好避免在不需要时使用'self'。如果读者被'to_a'弄糊涂而没有'self',他们会很快弄清楚,并且在讨价还价中学到一些东西。 –