Bei einem gegebenen Array erhält ein einzelnes Element oder Null ein Array - die beiden letzteren sind ein einzelnes Elementarray bzw. ein leeres Array.
Ich dachte fälschlicherweise, Ruby würde so funktionieren:
[1,2,3].to_a #= [1,2,3] # Already an array, so no change
1.to_a #= [1] # Creates an array and adds element
nil.to_a #= [] # Creates empty array
Aber was Sie wirklich bekommen, ist:
[1,2,3].to_a #= [1,2,3] # Hooray
1.to_a #= NoMethodError # Do not want
nil.to_a #= [] # Hooray
Um dies zu lösen, muss ich entweder eine andere Methode verwenden oder ich kann ein Meta-Programm erstellen, indem ich die to_a-Methode aller Klassen ändere, die ich verwenden möchte - was für mich keine Option ist.
Also eine Methode ist es:
result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))
Das Problem ist, dass es ein bisschen chaotisch ist. Gibt es eine elegante Möglichkeit, dies zu tun? (Ich würde mich wundern, wenn dies der rubinrote Weg ist, um dieses Problem zu lösen.)
Welche Anwendungen hat das? Warum überhaupt in ein Array konvertieren?
In Rails 'ActiveRecord gibt der Aufruf von say user.posts
entweder ein Array von Posts, einen einzelnen Post oder null zurück. Wenn Sie Methoden schreiben, die an den Ergebnissen arbeiten, ist es am einfachsten anzunehmen, dass die Methode ein Array verwendet, das null, eins oder viele Elemente enthalten kann. Beispielmethode:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
user.posts
sollte niemals einen einzelnen Beitrag zurückgeben. Zumindest habe ich es nie gesehen.==
statt=
, oder?[1,2,3].to_a
kommt nicht zurück[[1,2,3]]
! Es kehrt zurück[1,2,3]
.Antworten:
[*foo]
oderArray(foo)
wird die meiste Zeit funktionieren, aber in einigen Fällen wie einem Hash bringt es es durcheinander.Ich kann mir nur vorstellen, dass dies auch für einen Hash funktioniert, indem ich eine Methode definiere.
quelle
ensure_array
verlängernto_a
to_a
. Zum Beispiel{a: 1, b: 2}.each ...
würde anders funktionieren.Mit ActiveSupport (Rails):
Array.wrap
Wenn Sie Rails nicht verwenden, können Sie eine eigene Methode definieren, die der Rails-Quelle ähnelt .
quelle
class Array; singleton_class.send(:alias_method, :hug, :wrap); end
für zusätzliche Niedlichkeit.Die einfachste Lösung ist die Verwendung
[foo].flatten(1)
. Im Gegensatz zu anderen vorgeschlagenen Lösungen funktioniert es gut für (verschachtelte) Arrays, Hashes undnil
:quelle
Kernel#Array
dhArray()
ist der schnellste von allen. Ruby 2.5.1-Vergleich: Array (): 7936825.7 i / s. Array.wrap: 4199036.2 i / s - 1,89x langsamer. Wrap: 644030,4 i / s - 12,32x langsamerArray(whatever)
sollte den Trick machenquelle
ActiveSupport (Rails)
ActiveSupport hat dafür eine ziemlich gute Methode. Es ist voller Rails, also trotzig der schönste Weg, dies zu tun:
Splat (Ruby 1.9+)
Der splat-Operator (
*
) hebt die Anordnung eines Arrays auf, wenn dies möglich ist:Ohne Array macht es natürlich seltsame Dinge, und die Objekte, die Sie "splat", müssen in Arrays abgelegt werden. Es ist etwas seltsam, aber es bedeutet:
Wenn Sie nicht über ActiveSupport verfügen, können Sie die Methode definieren:
Wenn Sie jedoch große Arrays und weniger Nicht-Array-Elemente planen, möchten Sie diese möglicherweise ändern. Die obige Methode ist bei großen Arrays langsam und kann sogar zu einem Überlauf Ihres Stacks führen (omg so meta). Auf jeden Fall möchten Sie dies stattdessen tun:
Ich habe auch einige Benchmarks mit und ohne Teneray-Operator.
quelle
SystemStackError: stack level too deep
für 1M Elemente (Rubin 2.2.3).Hash#values_at
mit 1M-Argumenten (mitsplat
) versucht , und es wird der gleiche Fehler ausgegeben.object.is_a? Array ? object : [*object]
?Array.wrap(nil)
kehrt[]
nicht zurücknil
: /Wie wäre es mit
quelle
[].push(anything).flatten(1)
würde funktionieren! Verschachtelte Arrays werden nicht abgeflacht!Mit dem Risiko, das Offensichtliche zu sagen und zu wissen, dass dies nicht der leckerste syntaktische Zucker ist, der jemals auf dem Planeten und in den umliegenden Gebieten gesehen wurde, scheint dieser Code genau das zu tun, was Sie beschreiben:
quelle
Sie können die Array-Methode von Object überschreiben
alles erbt Objekt, daher wird to_a nun für alles unter der Sonne definiert
quelle
Ich habe alle Antworten durchgesehen und arbeite meistens nicht in Ruby 2+
Aber elado hat die eleganteste Lösung, dh
Leider funktioniert dies aber auch nicht für Ruby 2+, da Sie eine Fehlermeldung erhalten
Also, um das zu beheben, müssen Sie benötigen.
quelle
Da die Methode
#to_a
für die beiden Hauptproblemklassen (Nil
undHash
) bereits vorhanden ist , definieren Sie einfach eine Methode für den Rest, indem Sie Folgendes erweiternObject
:und dann können Sie diese Methode einfach für jedes Objekt aufrufen:
quelle