2016-07-12 8 views
0

Ich habe eine eingehende Eingabe, die manchmal ein Array von Hashes ist und manchmal ist es nur ein einzelner Hash.Wie mit dem Fall zu behandeln, wenn die Eingabe zu reduzieren ist Hash anstelle von Array von Hashes

hsh = {"property"=>[{"name"=>"first_name", "value"=>"Joe"}, {"name"=>"last_name", "value"=>"Doe"}]} 

oder

hsh = {"property"=>{"name"=>"Foo", "value"=>"Bar"}} 

Von diesem Eingang ich einen Hash mit name als key zu erzeugen versuchen und value und als value, etwa wie folgt:

hsh['property'].reduce(HashWithIndifferentAccess.new) do |scan, kv_pair| 
    scan.merge kv_pair['name'] => kv_pair['value'] 
end 

Giving:

{"first_name"=>"Joe", "last_name"=>"Doe"} 

Dies funktioniert gut, wenn hsh['property'] ein Array von Hashes ist aber nicht mit dem folgenden Fehler:

TypeError: no implicit conversion of String into Integer 

wenn es nur ein Hash ist.

Wie handhabe ich die reduce auf hsh['property'] so behandelt es den Fall, wenn die Eingabe ist es Hash-Hash anstelle von Array von Hashes?

+0

Wahrscheinlich nur den Typ überprüfen. I.e. 'if value.is_a? (Hash) ... else' –

Antwort

1

ich den eingehenden Parameter auf ein Array von einem einzigen Hash konvertieren würde, dann gehen Sie vor:

def foo(aoh) 
    aoh = [aoh] unless Array === aoh 

    aoh # return it so we can see it's been changed 

    # do stuff with the AoH 
end 

foo({a:1}) # => [{:a=>1}] 
foo([{a:1},{b:2}]) # => [{:a=>1}, {:b=>2}] 

Sobald es ein Array ist Wenn Sie einen einzelnen Hash oder mehrere Hashes verwenden, können Sie mit demselben Code über den Inhalt des Arrays iterieren.

Wenn Sie für die Generierung des einzelnen Hashs oder eines Arrays von Hashes verantwortlich sind, sollten Sie immer den gleichen Objekttyp generieren, ein Array von Hashes, was Ihnen den Umgang damit erleichtert. Wenn nicht, wandle es in den gleichen Objekttyp um und gehe weiter.

+0

Ich bin teilweise verantwortlich, dh ich benutze 'httparty' gem, um einen API-Endpunkt zu treffen, der ein XML zurückgibt. Die geparste Antwort des httparty-Antwortobjekts ist diejenige, die diesen Hash bildet. – harshs08

+1

Dann ändern Sie sofort den zurückgegebenen Wert, so dass Sie ein konsistentes Ergebnis haben. Lebe nicht mit dem Elend, in die eine oder andere Richtung springen zu müssen, weil sich der Datentyp ändert. –

4

Es ist einfach sicherzustellen, dass etwas ein Array ist. Du brauchst kein Wenn-sonst. Wickeln Sie es einfach in ein neues Array und reduzieren Sie das Array 1 Ebene nach unten. Ich muss zugeben, dass dies nicht sehr performant ist, aber es ist mir egal, ob das, was ich ursprünglich machen möchte, O (n) oder höher ist (wie Mapping, Reduzieren, Sortieren, was auch immer das Array mindestens einmal durchlaufen muss). Ich werde diesen Weg nicht gehen, wenn das, was ich ursprünglich machen möchte, O (log (n)) oder niedriger (wie binäre Suche) ist.

[hsh['property']].flatten(1).reduce(HashWithIndifferentAccess.new) do |scan, kv_pair| 
    scan.merge kv_pair['name'] => kv_pair['value'] 
end 
1

Das erste, was zu beheben ist von merge loszuwerden, die jedes Mal durch die Schleife eines Zwischen Hash erzeugt. Das ist eine Menge Müll zu sammeln. Die zweite Sache ist die Verwendung der einfacheren Methode each_with_index, die keine Verkettung erfordert. Auf diese Weise können nur Daten hinzufügen:

hsh['property'].each_with_object(HashWithIndifferentAccess.new) do |kv_pair, scan| 
    scan[kv_pair['name']] = kv_pair['value'] 
end 

Das funktioniert so lange, wie property eine Reihe von Hash-Objekte in einem Array hat. Wenn Sie nur eine haben müssen Sie zu Sonderfall, dass:

case hsh['property'] 
when Hash 
    HashWithIndifferentAccess.new(
    hsh['property']['name'] => hsh['property']['value'] 
) 
when Array 
    hsh['property'].each_with_object(HashWithIndifferentAccess.new) do |kv_pair, scan| 
    scan[kv_pair['name']] = kv_pair['value'] 
    end 
else 
    # Uh-oh, you've got to handle this case of something random. 
end 
+0

Der 'Array'-Teil 'properties.each_with_index (HashWithIndifferentAccess.new) wird | scan, kv_pair | scan [kv_pair ['Name']] = kv_pair ['Wert'] Ende' Fehler mit 'ArgumentError Exception: falsche Anzahl von Argumenten (gegeben 1, erwartet 0)'. Scheint so als ob "each_with_index" kein Argument benötigt. – harshs08

+0

Wird es "each_object" statt "each_with_index" sein. – harshs08

+0

@ harcs08 Sie sind genau richtig, mein Tippfehler. Fest. – tadman

0

Da Sie Schienen verwenden, gibt es eine dedizierte Methode, um sicherzustellen, dass etwas ein Array ist - Array#wrap. Beachten Sie auch, dass Ihre Verwendung von #reduce als nicht indikativ gilt und Sie stattdessen #each_with_object verwenden sollten.Hier

ist eine mögliche Lösung:

Array.wrap(hsh['property']).map { |kv| ['name', 'value'].map(&kv) }.to_h 

:

Array.wrap(hsh['property']).map { |kv| [kv['name'], kv['value']] }.to_h 

Wenn Sie mit Rubin 2.3.0+, Sie auch die Vorteile der neuen coolen Hash#to_proc nehmen

Wenn Sie darauf bestehen, können Sie die #with_indifferent_access am Ende hinzufügen.