142 votes

Ruby - convertit élégamment une variable en tableau si ce n'est pas déjà un tableau

Étant donné un tableau, un seul élément, ou nil, obtient un tableau - les deux derniers étant respectivement un tableau à un seul élément 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 est :

[1,2,3].to_a  #= [1,2,3]         # Hooray
1.to_a        #= NoMethodError   # Do not want
nil.to_a      #= []              # Hooray

Pour résoudre ce problème, je dois donc soit utiliser une autre méthode, soit métaprogrammer en modifiant la méthode to_a de toutes les classes que j'ai l'intention d'utiliser - ce qui n'est pas une option pour moi.

C'est donc une Méthode :

result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))

Le problème, c'est que c'est un peu le bazar. Existe-t-il une manière élégante de procéder ? (Je serais étonné si c'était la façon Ruby-ish de résoudre ce problème).


Quelles sont les applications ? Pourquoi même convertir en tableau ?

Dans ActiveRecord de Rails, l'appel dit, user.posts retournera soit un tableau de messages, un seul message, ou nil. Lorsque vous écrivez des méthodes qui travaillent sur les résultats de cette méthode, il est plus facile de supposer que la méthode prendra un tableau, qui peut avoir zéro, un ou plusieurs éléments. Exemple de méthode :

current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}

172voto

sawa Points 62592

[*foo] ou Array(foo) fonctionnera la plupart du temps, mais dans certains cas, comme pour un hachage, cela ne fonctionnera pas.

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]]

Le seul moyen auquel je pense et qui 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}]

140voto

elado Points 1896

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 comme suit la source des rails .

class Array
  def self.wrap(object)
    if object.nil?
      []
    elsif object.respond_to?(:to_ary)
      object.to_ary || [object]
    else
      [object]
    end
  end
end

21voto

olito Points 151

La solution la plus simple est d'utiliser [foo].flatten(1) . Contrairement à d'autres solutions proposées, elle fonctionnera bien pour les tableaux (imbriqués), les hachages et les fichiers de données. 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'}]

20voto

Benjamin Gruenbaum Points 51406

Array(whatever) devrait faire l'affaire

Array([1,2,3]) # [1,2,3]
Array(nil) # []
Array(1337)   # [1337]

13voto

penne12 Points 1659

ActiveSupport (Rails)

ActiveSupport dispose d'une méthode intéressante pour cela. Il est chargé avec Rails, donc certainement la meilleure façon de le faire :

Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil

Splat (Ruby 1.9+)

L'opérateur splat ( * ) désarticule un tableau, s'il le peut :

*[1,2,3] #=> 1, 2, 3 (notice how this DOES not have braces)

Bien sûr, sans tableau, il fait des choses bizarres, et les objets que vous "splattez" doivent être placés dans des tableaux. C'est un peu bizarre, mais ça veut dire quelque chose :

[*[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

Cependant, si vous prévoyez d'avoir de grands tableaux et moins de choses qui ne sont pas des tableaux, vous pourriez vouloir le changer - la méthode ci-dessus est lente avec les grands tableaux et peut même faire déborder votre pile (omg so meta). Quoi qu'il en soit, vous pourriez vouloir 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 repères avec et sans l'opérateur Teneray.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X