2009-11-18 13 views
45

Ich habe aa HashHash [ 'key'] hash.key in Ruby

foo = {'bar'=>'baz'} 

I foo.bar #=> 'baz'

Meine Motivation eine Activerecord-Abfrage Umschreiben nennen möchte in einen rohen SQL-Abfrage (mit Modell # find_by_sql). Dies gibt einen Hash mit den SELECT-Klauselwerten als Schlüssel zurück. Mein bestehender Code basiert jedoch auf der Objektpunktpunkt-Notation. Ich möchte minimal Code neu schreiben. Vielen Dank.

Edit: es erscheint Lua diese Funktion hat:

point = { x = 10, y = 20 } -- Create new table 
print(point["x"])   -- Prints 10 
print(point.x)    -- Has exactly the same meaning as line above 
+4

Warum ist es wichtig, dass Lua dieses "Feature" hat? So auch JavaScript. Es ist nicht besonders relevant für die Frage. –

+5

Ich habe nur versucht, besser darzustellen, was ich mit Ruby erreichen wollte. – user94154

Antwort

81
>> require 'ostruct' 
=> [] 
>> foo = {'bar'=>'baz'} 
=> {"bar"=>"baz"} 
>> foo_obj = OpenStruct.new foo 
=> #<OpenStruct bar="baz"> 
>> foo_obj.bar 
=> "baz" 
>> 
+3

Die Verwendung von OpenStruct wird abgeraten, da jede Instanziierung den Ruby-Methodencache ungültig macht, was die gesamte Anwendung verlangsamt. –

+0

@ChrisHeald: Möchten Sie sich etwas zu diesem Vorschlag äußern? Wer entmutigt den Einsatz von OpenStruct? Verletzt es den * gesamten * Methodencache oder nur den Methodencache für das Symbol? – Steen

+4

@Steen Wenn du Google "ruby method cache" googelst, werden die ersten paar Hits Erklärungen von Charlie Somerville und dem verstorbenen James Golick sein. Vor Ruby 2.1 hat das Instanziieren einer ostruct-Methode den * gesamten * Methodencache ausgegeben. In 2.1+ wird der Methodencache für alle ostruct-Instanzen und Instanzen seiner Unterklassen ausgegeben. –

6

Anstatt die alle Sachen aus dem Hash zu kopieren, können Sie fügen Sie einfach einige Verhalten Lookups zu tun, um zu Hash.

Wenn Sie diese Defintion hinzufügen, können Sie Hash erweitern alle unbekannten Methoden als Hash-Lookups zu behandeln:

class Hash 
    def method_missing(n) 
    self[n.to_s] 
    end 
end 

Beachten Sie, dass dies bedeutet, dass Sie nicht immer Fehler angezeigt wird, wenn Sie die falsche Methode anrufen Hash - Sie erhalten nur, was auch immer der entsprechende Hash-Lookup zurückgeben würde.

Sie können in beträchtlichem Ausmaß die Debugging-Probleme reduzieren dies nur durch verursachen kann das Verfahren auf einen bestimmten Hash setzen - oder so viele Hashes, wie Sie benötigen:

a={'foo'=>5, 'goo'=>6} 
def a.method_missing(n) 
    self[n.to_s] 
end 

Die andere Beobachtung, dass, wenn method_missing aufgerufen wird durch das System gibt es Ihnen ein Symbol Argument. Mein Code hat es in eine String umgewandelt. Wenn Ihre Hash-Schlüssel keine Zeichenfolgen sind, gibt dieser Code niemals diese Werte zurück - wenn Sie nach Symbolen anstelle von Zeichenfolgen suchen, ersetzen Sie einfach n für n.to_s oben.

+13

Jemand hat das schon früh in einem großen Projekt getan, an dem ich gearbeitet habe. Es verursachte alle Arten von subtilen Fehlern, weil falsche Methodenaufrufe bei irgendeinem Hash im System nur Null zurückgaben, anstatt uns mit einem NoMethodError zu alarmieren. Es hat ewig gedauert, das System zu entfernen, weil der Code im ganzen System davon abhängig geworden war. Es ist eine der katastrophalsten Code-Klassen-Erweiterungen, die ich je gesehen habe. – Avdi

+0

Äh, das hätte "core-class extensions" lesen sollen. – Avdi

+4

Um (meistens) das Problem zu beheben, könnten Sie 'if has_key? n.to_s dann self [n.to_s] sonst raise NoMethodError' – jtbandes

35

Was Sie suchen, heißt OpenStruct. Es ist Teil der Standardbibliothek.

22

Eine gute Lösung:

class Hash 
    def method_missing(method, *opts) 
    m = method.to_s 
    if self.has_key?(m) 
     return self[m] 
    elsif self.has_key?(m.to_sym) 
     return self[m.to_sym] 
    end 
    super 
    end 
end 

Hinweis: Diese Implementierung hat nur einen bekannten Fehler:

x = { 'test' => 'aValue', :test => 'bar'} 
x.test # => 'aValue' 

Wenn Sie Symbol anstatt Lookups Zeichenfolge Lookups bevorzugen, tauschen dann die beiden " wenn 'Bedingung

+0

Hätte sein Problem vielleicht nicht gelöst, aber das hat eine meiner gelöst. Ich habe viel gesucht, um das zu finden, aber ich danke dir! – gregf

+3

Ich habe dieses 'self [m] || gemacht Selbst [m.to_s] || super 'anstelle des' if/else'-Blocks – Kyle

+1

Das funktioniert nur, wenn der Hash-Schlüssel nicht mit einem existierenden Methodennamen übereinstimmt (wie Hash # zip, zum Beispiel). –

4

Es gibt ein paar Edelsteine ​​dafür. Es gibt mein letztes Juwel, hash_dot, und ein paar andere Edelsteine ​​mit ähnlichen Namen, die ich entdeckte, als ich meins auf RubyGems veröffentlichte, einschließlich dot_hash.

HashDot ermöglicht Punktnotationssyntax, während die Probleme von NoMethodErrors behoben werden, die von @avdi angesprochen werden. Es ist schneller und durchlässiger als ein mit OpenStruct erstelltes Objekt.

require 'hash_dot' 
a = {b: {c: {d: 1}}}.to_dot 
a.b.c.d => 1 

require 'open_struct' 
os = OpenStruct.new(a) 
os.b => {c: {d: 1}} 
os.b.c.d => NoMethodError 

Er behält auch das erwartete Verhalten bei, wenn Nicht-Methoden aufgerufen werden.

a.non_method => NoMethodError 

Bitte zögern Sie nicht, Verbesserungen oder Fehler an HashDot zu senden.