2009-04-02 12 views
62

Ich versuche, herauszufinden, wie ich Schlüssel und Wert herausfiltern kann Paare von einem Filter in eine anderenRuby-Hash Whitelist Filter

Zum Beispiel habe ich diesen Hash

x = { "one" => "one", "two" => "two", "three" => "three"} 

y = x.some_function 

y == { "one" => "one", "two" => "two"} 

Danke für Ihre Hilfe nehmen will

EDIT: sollte wahrscheinlich erwähnen, dass ich in diesem Beispiel möchte, dass es sich wie ein Whitelist-Filter verhält. Das heißt, ich weiß was ich will, nicht was ich nicht will.

+0

klingt wie ein Duplikat dieser: http: // Stackoverflow.com/questions/7430343/ruby-einfachste-weg-zu-filter-hash-keys/# antwort-30551883 – metakungfu

Antwort

48

Vielleicht ist es das, was Sie wollen.

wanted_keys = %w[one two] 
x = { "one" => "one", "two" => "two", "three" => "three"} 
x.select { |key,_| wanted_keys.include? key } 

Die aufzählbare Mischung, die z. Array and Hash bietet viele nützliche Methoden wie Auswählen/Ablehnen/Each/etc .. Ich schlage vor, dass Sie die Dokumentation dafür mit Ri Enumerable betrachten.

+0

aber die select-Methode gibt ein Array zurück. In diesem Fall brauche ich einen Hash – stellard

+0

Ah, sorry, verpasste dieses Bit. – sris

+0

Dies funktioniert in 1.9.2 jetzt, aber immer noch nicht in 1.8.7 – stellard

48

Sie können einfach die eingebaute Hash-Funktion Reject verwenden.

x = { "one" => "one", "two" => "two", "three" => "three"} 
y = x.reject {|key,value| key == "three" } 
y == { "one" => "one", "two" => "two"} 

können Sie setzen, was Logik, die Sie in die ablehnen wollen, und wenn der Block true zurück, wird es diesen Schlüssel, Wert in der neuen Hash überspringen.

+1

5 sekunden hinter dem tippen ... Ich wusste, ich hätte vor dem Testen geschrieben, um sicherzustellen, dass ich nicht gemacht habe ein Tippfehler. –

+0

Sie können 'reject!' Anstelle einer anderen Variablen verwenden –

+2

@Radar Destruktive Modifikatoren können Probleme verursachen, wenn zum Beispiel der Hash als Argument einer Methode übergeben wird und der Aufrufer nicht erwartet, dass der Hash dadurch geändert wird Methode. Am besten gewöhnt man sich an zerstörungsfreien Updates und benutzt nur zerstörerische Operatoren wenn nötig. –

7
y = x.reject {|k,v| k == "three"} 
6

eine Kombination aller Antworten Mit ich mit dieser Lösung gekommen sind:

wanted_keys = %w[one two] 
x = { "one" => "one", "two" => "two", "three" => "three"} 
x.reject { |key,_| !wanted_keys.include? key } 
=>{ "one" => "one", "two" => "two"} 
für Ihre Hilfe Jungs

Dank!

EDIT:

Die oben genannten Arbeiten in 1.8.7+

folgende Arbeiten in 1.9+:

x.select {| Schlüssel, _ | wanted_keys.include? key}

+1

Warum die doppelte negative? 'x.select {| key, _ | wanted_keys.include? Schlüssel} '? Dies gibt einen Hash für mich –

+0

Es hängt von der Ruby-Version, die Sie verwenden. Dies funktioniert in 1.9+, aber nicht in 1.8.7. Ich werde die Antwort bearbeiten – stellard

+0

Downvote für die Annahme Ihrer eigenen Kopie von @ sris Antwort! Ihr Kommentar zu dieser Antwort ist ausreichend, um auf den Unterschied hinzuweisen. – RobinGower

96

Rails' auch Active Bibliothek gibt Ihnen in Scheiben schneiden und mit der Ausnahme auf einem Schlüssel Ebene mit dem Hash für den Umgang:

y = x.slice("one", "two") # => { "one" => "one", "two" => "two" } 
y = x.except("three")  # => { "one" => "one", "two" => "two" } 
x.slice!("one", "two") # x is now { "one" => "one", "two" => "two" } 

ganz nett Dies sind, und ich verwende sie die ganze Zeit.

+0

Und wenn x eine ActiveRecord :: Base-Unterklasse ist, können Sie 'y = x.attributes.slice *% w (eins zwei drei)' tun. –

+1

Wenn Sie mit ['_.pick' in Underscore.js] (http://underscorejs.org/#pick) vertraut sind, ist dies die gleiche Idee. (Ich kam hierher, um das zu suchen!) –

+0

Und 'ActiveSupport' ist ziemlich leicht, es wird von vielen Nicht-Rails-Edelsteinen benutzt. – skalee

6

Verbessern Sie ein Bit @scottd Antwort, wenn Sie Schienen verwenden und haben eine Liste von was Sie brauchen, können Sie die Liste als Parameter aus Slice erweitern. Zum Beispiel

hash = { "one" => "one", "two" => "two", "three" => "three"} 
keys_whitelist = %W(one two) 
hash.slice(*keys_whitelist) 

Und ohne Schienen, für jede Ruby-Version, können Sie wie folgt vorgehen:

hash = { "one" => "one", "two" => "two", "three" => "three"} 
keys_whitelist = %W(one two) 
Hash[hash.find_all{|k,v| keys_whitelist.include?(k)}] 
+0

Hinweis: Sie müssen nicht unbedingt * Rails * verwenden, Sie können nur Active Support laden. Füge 'active_support' zu deiner Gemdatei hinzu und benötige" active_support/core_ext/hash/slice "'. – GMA

+0

'find_all' ist ein Alias ​​für' select', also ist das im Prinzip das selbe wie die ersten beiden Antworten :-) – AlexChaffee

0

ich eine Lambda verwenden würde zu filtern. Dies ermöglicht Ihnen, komplexe Filterlogik zu schreiben & machen es einfach, es zu testen. Die Tatsache, dass die Filterlogik extrahiert wird, erlaubt es, sie in anderen Kontexten wiederzuverwenden.

ex:

x = { "one" => "one", "two" => "two", "three" => "three"} 

matcher = ->(key,value) { 
    # FILTERING LOGIC HERE 
    !key[/three/] 
} 

x.select(&matcher) == { "one" => "one", "two" => "two"} 
Verwandte Themen