2016-04-29 16 views
2

Ich habe folgendes:Wie verwende ich eine String-Beschreibung, um auf Daten von einer Hash-in- Hash-Struktur zuzugreifen?

data_spec['data'] = "some.awesome.values" 

data_path = "" 
data_spec['data'].split('.').each do |level| 
    data_path = "#{data_path}['#{level}']" 
end 

data = "site.data#{data_path}" 

An diesem Punkt data entspricht einem String: "site.data['some']['awesome']['values']"

Was ich brauche Hilfe mit der Zeichenfolge wird mit dem Wert zu erhalten: site.data['some']['awesome']['values']

site.data hat der folgende Wert:

{ 
    "some" => { 
     "awesome" => { 
      "values" => [ 
       { 
        "things" => "Stuff", 
        "stuff" => "Things", 
       }, 
       { 
        "more_things" => "More Stuff", 
        "more_stuff" => "More Things", 
       } 
      ] 
     } 
    } 
} 

Jede Hilfe ist Grea tally geschätzt. Vielen Dank!

+0

könnten Sie verwenden ein 'eval', aber das ist nicht wirklich ein guter Weg, um darüber zu gehen. Ist Ihr tatsächliches Problem, den Wert 'site.data ['some'] ['awesome'] ['values']' zu übergeben, wenn der Eingabe-String '" some.awesome.values ​​"'? –

+0

Ich bin mir nicht sicher, ob ich vollständig verstehe, ich bin sehr neu in Ruby. Die Datenspez ['Daten'] wird aus einer YAML-Datei gezogen. –

Antwort

4

Sie tun könnten als Tadman vorgeschlagen und site.data.dig('some', 'awesome', values') verwenden, wenn Sie Rubin verwenden 2.3.0 (das ist genial und ich nicht einmal existiert hat wissen). Dies ist wahrscheinlich die beste Wahl. Aber wenn Sie den Code wirklich selbst schreiben möchten, lesen Sie weiter unten.

Sie auf dem richtigen Weg waren, der beste Weg, dies zu tun ist:

data_spec['data'] = "some.awesome.values" 

data = nil 
data_spec['data'].split('.').each do |level| 
    if data.nil? 
    data = site.data[level] 
    else 
    data = data[level] 
    end 
end 

Um zu verstehen, warum das funktioniert zuerst müssen Sie verstehen, dass site.data['some']['awesome']['values'] die gleiche wie zu sagen: erster some im Innern, die dann erhalten Holen Sie awesome dann innerhalb, dass values bekommen. Also unser erster Schritt ist das Abrufen der some. Da wir diese erste Ebene noch nicht haben, bekommen wir sie von site.data und speichern sie in einer Variablen data. Sobald wir das haben, holen wir einfach jede Ebene danach aus den Daten und speichern sie in Daten, was uns erlaubt, tiefer und tiefer in den Hash zu kommen.

Ihr Beispiel also mit data wie diese initally aussehen:

 {"awesome" => { 
      "values" => [ 
       { 
        "things" => "Stuff", 
        "stuff" => "Things", 
       }, 
       { 
        "more_things" => "More Stuff", 
        "more_stuff" => "More Things", 
       } 
      ] 
     } 
    } 

Dann folgt aus:

 {"values" => [ 
      { 
       "things" => "Stuff", 
       "stuff" => "Things", 
      }, 
      { 
       "more_things" => "More Stuff", 
       "more_stuff" => "More Things", 
      } 
     ] 
    } 

und schließlich Ausgabe wie folgt:

 [ 
      { 
       "things" => "Stuff", 
       "stuff" => "Things", 
      }, 
      { 
       "more_things" => "More Stuff", 
       "more_stuff" => "More Things", 
      } 
     ] 
+0

Dies ist auf dem richtigen Weg. Wenn das ursprüngliche String-Format von 'x ['y'] ['z'] 'behandelt werden muss, wird dies vermutlich einen regulären Ausdruck irgendeiner Art erfordern. – tadman

+0

Ich glaube nicht, dass man benötigt wird, solange OP das Format 'foo.bar.foobar' verwenden will und sich aufteilt. –

+0

Guter Punkt. Die Zwischenform war, was mich abwarf. – tadman

-2

Was Sie suchen, ist etwas, auf das allgemein und aus guten Gründen herabgesehen wird. Aber hier geht - es eval genannt:

binding.eval data 
+0

es ist eine schlechte Praxis, 'eval' in Ruby-Code zu verwenden. – Ilya

+2

Dies ist vollständig über-töten und ist eine gebrochene Lösung, die Sie schämen sollten, um zu empfehlen. Es gibt einen * Grund *, auf den man herabschaut. – tadman

+0

@Ilya, als ob ich es nicht erwähnt hätte. Es ist nur die richtige Antwort auf die Frage * "Ich habe diese' Daten' Variable, wie bekomme ich den Code innerhalb ausgeführt "*. – ndn

4

Wenn Sie empfangen eine Zeichenfolge wie 'x.y.z' und müssen einen verschachtelten Hash, Rub 2.3.0 y umfasst die dig Methode:

spec = "some.awesome.values" 

data = { 
    "some" => { 
    "awesome" => { 
     "values" => [ 
      'a','b','c' 
     ] 
    } 
    } 
} 

data.dig(*spec.split('.')) 
# => ["a", "b", "c"] 

Wenn Sie nicht über Ruby-2.3.0 und Modernisierung ist keine Option, man kann es nur Patch in jetzt:

class Hash 
    def dig(*path) 
    path.inject(self) do |location, key| 
     location.respond_to?(:keys) ? location[key] : nil 
    end 
    end 
end 
0

I schrieb etwas, das genau das tut. Fühlen Sie sich frei, irgendwelche Informationen von Wert zu nehmen oder es zu stehlen!:)

https://github.com/keithrbennett/trick_bag/blob/master/lib/trick_bag/collections/collection_access.rb

Überprüfen Sie die Unit-Tests zu sehen, wie es zu benutzen:

https://github.com/keithrbennett/trick_bag/blob/master/spec/trick_bag/collections/collection_access_spec.rb

Es gibt eine accessor Methode, die eine Lambda zurückgibt. Da lambda kann mit dem [] Operator (Methode, wirklich) aufgerufen werden, können Sie ein solche Lambda und Zugang beliebige Anzahl von Ebenen erhalten:

accessor['hostname.ip_addresses.0'] 

oder in Ihrem Fall:

require 'trick_bag' 
accessor = TrickBag::CollectionsAccess.accessor(site.data) 
do_something_with(accessor['some.awesome.values']) 
Verwandte Themen