2014-04-17 12 views
11

Come posso sapere se un hash di Ruby è un sottoinsieme di (o include) un altro hash?Controllo hash rubino è sottoinsieme?

Ad esempio:

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

Matt, ti sembra di essere stato conferito con una [imbarazzo della scelta] (http: //dictionary.cambridge. org/Dizionario/britannico/un-imbarazzo-of-ricchezza). (Collegamento per coloro per i quali l'inglese è una seconda lingua). Ottenere quattro delle quattro risposte di qualità su SO è piuttosto raro. –

risposta

9

Dal Rubino 2.3 è anche possibile effettuare le seguenti operazioni per verificare se si tratta di un sottoinsieme

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

funziona con hash annidati? Altrimenti è sicuramente una risposta concisa. – nus

+0

@nus funziona con hash annidati. Tuttavia, l'hash nidificato deve essere uguale all'altro, quindi non può essere solo un sottoinsieme: '{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

Funziona anche al contrario: hash > = {f: 6, c: 3} ' –

16

La soluzione che mi venne in uso mente Hash#merge metodo:

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

Mi piace, Marek, in parte perché legge bene. –

+2

E per l'hash annidato è possibile utilizzare il 'supporto attivo' di [deep_merge] (http://apidock.com/rails/Hash/deep_merge) – Musaffa

+0

Questo è molto elegante. Io chiamo il mio 'superset?' Però, e ho creato anche 'sottoinsieme?'. – nus

7

è possibile convertire gli hash per sets e di eseguire il controllo utilizzando i metodi subset? e superset? (oi loro rispettivi alias <= e >=):

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 

Aggiornamento: un punto di riferimento delle soluzioni proposte:

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) } } 
) 

Risultato:

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

questo è bello :-) –

+0

Grazie! Basato su "Set # superset?", Dovrei chiamare il metodo "Hash # superset?" Invece di "Hash # include_hash?". – ma11hew28

+0

Probabilmente "superset?" Sarebbe un nome migliore, tuttavia penso che sia solo una questione di gusti. – toro2k

6

si può fare:

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

Qualcuno può controllare se 'subset_hash.all? {| coppia | arr.include? (pair)} 'funziona anche o meno ... Sono lontano dal computer, quindi non ho potuto testare lo stesso .. –

+0

Ho controllato con alcuni casi di test, e sembra funzionare correttamente. – ma11hew28

+0

@MattDiPasquale Grazie ... L'ho aggiornato ora .. –

8

differenza Array sembra più semplice:

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

Capisco '(h.to_a - to_a)' .. Ma per una migliore leggibilità '(h.to_a - self.to_a)' ... IMO –

+3

@Arup, sono rispettosamente in disaccordo, anche se so che il tuo punto di vista è condiviso da molti altri. Imo, 'self.to_a' suggerisce che' self' è necessario qui (come sarebbe se fosse seguito da un accessore o 'classe'). Penso che sia sempre meglio evitare di usare 'self' quando non è richiesto.Se un lettore viene confuso da 'to_a' senza' self', lo scopriranno rapidamente e impareranno qualcosa nel vero affare. –

Problemi correlati