2014-04-17 15 views
11

Wie kann ich feststellen, ob ein Ruby-Hash eine Teilmenge eines anderen Hash ist oder einen anderen enthält?Ruby Hash Check ist Teilmenge?

Zum Beispiel:

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, Sie scheinen mit einer [Qual der Wahl] (http verliehen worden zu sein: //dictionary.cambridge. org/dictionary/british/an-peinlichkeit-von-riches). (Link für diejenigen, für die Englisch eine zweite Sprache ist.) Es ist ziemlich selten, vier von vier qualitativen Antworten auf SO zu bekommen. –

Antwort

9

Da Ruby-2.3 Sie können auch die folgende überprüfen tun, wenn diese eine Teilmenge

ist
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

funktioniert das mit verschachtelten Hashes? Ansonsten ist es definitiv eine knappe Antwort. – nus

+0

@nus es funktioniert mit verschachtelten Hashes. Der verschachtelte Hash muss jedoch dem anderen entsprechen. Daher kann es nicht nur eine Teilmenge sein: '{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

Funktioniert auch andersherum:' hash > = {f: 6, c: 3} ' –

16

Die Lösung, die ich in dem Sinn verwendet Hash#merge Methode kam:

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

Ich mag das, Marek, teilweise weil es gut liest. –

+2

Und für verschachtelte Hash können Sie 'aktive Unterstützung''s [Deep_merge] (http://apidock.com/rails/Hash/deep_merge) – Musaffa

+0

Dies ist sehr elegant. Ich nenne jedoch meine "Obermenge" und 'Untermenge?'. – nus

7

Sie könnten die Hashes zu sets konvertieren und als führten die Überprüfung mit den Methoden subset? und superset? (oder ihre jeweiligen Pseudonyme <= und >=):

aktualisieren: ein Benchmark der vorgeschlagenen Lösungen:

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

Ergebnis:

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

das ist cool :-) –

+0

Danke! Basierend auf 'Set # superset?', Sollte ich die Methode 'Hash # superset 'statt' Hash # include_hash? 'Aufrufen. – ma11hew28

+0

Wahrscheinlich "Supersatz" wäre ein besserer Name, aber ich denke, es ist nur eine Frage des Geschmacks. – toro2k

6

können Sie tun:

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

Kann jemand überprüfen, ob 'subset_hash.all? {| Paar | arr.include? (Paar)} 'funktioniert auch oder nicht ... Ich bin weg vom Computer, also konnte nicht das gleiche testen .. –

+0

Ich habe mit einigen Testfällen überprüft, und es scheint richtig zu funktionieren. – ma11hew28

+0

@MattDiPasquale Danke ... Ich habe es jetzt aktualisiert .. –

8

Array Unterschied scheint am einfachsten:

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

Ich verstehe '(h.to_a - to_a)' .. Aber für bessere Lesbarkeit '(h.to_a - self.to_a)' ... IMO –

+3

@Arup, ich respektvoll nicht zustimmen, obwohl ich weiß, Ihre Ansicht wird von vielen geteilt Andere. Imo, 'self.to_a', legt nahe, dass 'self' hier gebraucht wird (wie es wäre, wenn es von einem Accessor oder' class' gefolgt wäre). Ich denke, es ist immer am besten zu vermeiden, "Selbst" zu verwenden, wenn es nicht benötigt wird.Wenn ein Leser von "to_a" ohne "sich selbst" verwirrt wird, werden sie es schnell herausfinden und etwas dazu lernen. –