À partir d'un tableau, d'un seul élément ou de nil, obtenez un tableau - les deux derniers étant respectivement un tableau à élément unique et un tableau vide.
J'ai pensé à tort que Ruby fonctionnerait de cette façon:
[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
Mais ce que vous obtenez vraiment, c'est:
[1,2,3].to_a #= [1,2,3] # Hooray
1.to_a #= NoMethodError # Do not want
nil.to_a #= [] # Hooray
Donc, pour résoudre ce problème, je dois soit utiliser une autre méthode, soit méta-programme en modifiant la méthode to_a de toutes les classes que je compte utiliser - ce qui n'est pas une option pour moi.
Donc une méthode c'est:
result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))
Le problème est que c'est un peu le bordel. Y a-t-il une manière élégante de faire ceci? (Je serais étonné s'il s'agit de la manière la plus rubis de résoudre ce problème)
Dans ActiveRecord de Rails, appeler par exemple, user.posts
renverra un tableau de messages, un message unique ou nil. Lors de l'écriture de méthodes qui travaillent sur les résultats, il est plus facile de supposer que la méthode utilisera un tableau pouvant comporter zéro, un ou plusieurs éléments. Exemple de méthode:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
[*foo]
Ou Array(foo)
fonctionnera la plupart du temps, mais dans certains cas, comme un hachage, cela gâche tout.
Array([1, 2, 3]) # => [1, 2, 3]
Array(1) # => [1]
Array(nil) # => []
Array({a: 1, b: 2}) # => [[:a, 1], [:b, 2]]
[*[1, 2, 3]] # => [1, 2, 3]
[*1] # => [1]
[*nil] # => []
[*{a: 1, b: 2}] # => [[:a, 1], [:b, 2]]
La seule façon pour moi de penser que cela fonctionne même pour un hash est de définir une méthode.
class Object; def ensure_array; [self] end end
class Array; def ensure_array; to_a end end
class NilClass; def ensure_array; to_a end end
[1, 2, 3].ensure_array # => [1, 2, 3]
1.ensure_array # => [1]
nil.ensure_array # => []
{a: 1, b: 2}.ensure_array # => [{a: 1, b: 2}]
Avec ActiveSupport (Rails): Array.wrap
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(1) # => [1]
Array.wrap(nil) # => []
Array.wrap({a: 1, b: 2}) # => [{:a=>1, :b=>2}]
Si vous n'utilisez pas Rails, vous pouvez définir votre propre méthode similaire à the Rails source .
class Array
def self.wrap(object)
if object.nil?
[]
elsif object.respond_to?(:to_ary)
object.to_ary || [object]
else
[object]
end
end
end
La solution la plus simple consiste à utiliser [foo].flatten(1)
. Contrairement aux autres solutions proposées, cela fonctionnera bien pour les tableaux (imbriqués), les hachages et nil
:
def wrap(foo)
[foo].flatten(1)
end
wrap([1,2,3]) #= [1,2,3]
wrap([[1,2],[3,4]]) #= [[1,2],[3,4]]
wrap(1) #= [1]
wrap(nil) #= [nil]
wrap({key: 'value'}) #= [{key: 'value'}]
Array(whatever)
devrait faire l'affaire
Array([1,2,3]) # [1,2,3]
Array(nil) # []
Array(1337) # [1337]
ActiveSupport a une jolie méthode pour cela. Il est chargé avec Rails, donc le meilleur moyen de le faire:
Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil
L'opérateur splat (*
) décompresse un tableau s'il peut:
*[1,2,3] #=> 1, 2, 3 (notice how this DOES not have braces)
Bien sûr, sans tableau, cela fait des choses étranges, et les objets que vous "splat" doivent être placés dans des tableaux. C'est un peu bizarre, mais cela signifie:
[*[1,2,3]] #=> [1, 2, 3]
[*5] #=> [5]
[*nil] #=> []
[*{meh: "meh"}] #=> [[:meh, "meh"], [:meh2, "lol"]]
Si vous n'avez pas ActiveSupport, vous pouvez définir la méthode:
class Array
def self.wrap(object)
[*object]
end
end
Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil
Bien que, si vous envisagez de disposer de grands tableaux et de moins d'éléments autres que des tableaux, vous voudrez peut-être le modifier - la méthode ci-dessus est lente avec les grands tableaux et peut même causer le débordement de votre pile (omg so méta). Quoi qu'il en soit, vous voudrez peut-être faire ceci à la place:
class Array
def self.wrap(object)
object.is_a? Array ? object : [*object]
end
end
Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> [nil]
J'ai aussi quelques benchmarks avec et sans l'opérateur teneray.
Que diriez-vous
[].Push(anything).flatten
Avec le risque de dire l'évidence, et sachant que ce n'est pas le sucre syntaxique le plus savoureux jamais vu sur la planète et ses environs, ce code semble faire exactement ce que vous décrivez:
foo = foo.is_a?(Array) ? foo : foo.nil? ? [] : [foo]
J'ai parcouru toutes les réponses et la plupart du temps, je ne travaille pas dans Ruby 2+
Mais Elado a la solution la plus élégante c'est-à-dire
Avec ActiveSupport (Rails): Array.wrap
Array.wrap ([1, 2, 3]) # => [1, 2, 3]
Array.wrap (1) # => [1]
Array.wrap (nil) # => []
Array.wrap ({a: 1, b: 2}) # => [{: a => 1,: b => 2}]
Malheureusement, cela ne fonctionne pas non plus pour Ruby 2+ car vous obtiendrez une erreur
undefined method `wrap' for Array:Class
Donc, pour résoudre ce problème, vous devez en avoir besoin.
nécessite 'active_support/deprecation'
require'support_actif/core_ext/array/wrap '
vous pouvez écraser la méthode array de Object
class Object
def to_a
[self]
end
end
tout hérite de Object, donc to_a sera maintenant défini pour tout sous le soleil
Depuis la méthode #to_a
existe déjà pour les deux principales classes problématiques (Nil
et Hash
), définissez simplement une méthode pour le reste en développant Object
:
class Object
def to_a
[self]
end
end
et alors vous pouvez facilement appeler cette méthode sur n’importe quel objet:
"Hello world".to_a
# => ["Hello world"]
123.to_a
# => [123]
{a:1, b:2}.to_a
# => [[:a, 1], [:b, 2]]
nil.to_a
# => []