строки Ruby-элегантно преобразовать переменную в массив, если не массив уже




основы синтаксиса ruby (8)

Учитывая массив, один элемент или nil получает массив - последние два представляют собой один элементный массив и пустой массив соответственно.

Я по ошибке подумал, что Руби будет работать так:

[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

Но вы действительно получаете:

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

Поэтому, чтобы решить эту проблему, мне нужно либо использовать другой метод, либо мета-программу, изменяя метод to_a всех классов, которые я намереваюсь использовать, - что для меня не является вариантом.

Таким образом, это метод:

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

Проблема в том, что это немного беспорядок. Есть ли элегантный способ сделать это? (Я был бы поражен, если это Ruby-ish способ решить эту проблему)

Какие приложения у этого есть? Почему даже преобразовать в массив?

В ActiveRecord Rails, вызывая, например, user.posts , вернет массив сообщений, один пост или нуль. При написании методов, которые работают с результатами этого, проще всего предположить, что метод примет массив, который может иметь нулевой, один или несколько элементов. Пример метода:

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

ActiveSupport (Rails)

Для этого у ActiveSupport есть довольно хороший метод. Он загружен с помощью Rails, поэтому вызывающе красивейший способ сделать это:

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

Splat (Ruby 1.9+)

Оператор splat ( * ) не массирует массив, если он может:

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

Конечно, без массива, он делает странные вещи, и объекты, которые вы «splat», должны быть помещены в массивы. Это несколько странно, но это означает:

[*[1,2,3]] #=> [1, 2, 3]
[*5] #=> [5]
[*nil] #=> []
[*{meh: "meh"}] #=> [[:meh, "meh"], [:meh2, "lol"]]

Если у вас нет ActiveSupport, вы можете определить метод:

class Array
    def self.wrap(object)
        [*object]
    end
end

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

Хотя, если вы планируете иметь большие массивы и меньше не-массивные вещи, вы можете захотеть их изменить - вышеупомянутый метод медленный с большими массивами и даже может привести к переполнению стека (omg so meta). В любом случае, вы можете сделать это вместо этого:

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]

У меня также есть некоторые benchmarks с и без оператора teneray.


С риском заявить очевидное и зная, что это не самый вкусный синтаксический сахар, когда-либо замеченный на планете и прилегающих районах, этот код, похоже, делает именно то, что вы описываете:

foo = foo.is_a?(Array) ? foo : foo.nil? ? [] : [foo]

вы можете перезаписать метод массива объекта

class Object
    def to_a
        [self]
    end
end

все наследует Object, поэтому to_a теперь будет определяться для всего под солнцем


Я прошел все ответы и в основном не работал в рубине 2+

Но elado имеет самое элегантное решение, т.е.

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

К сожалению, это также не работает для ruby ​​2+, поскольку вы получите сообщение об ошибке

undefined method `wrap' for Array:Class

Поэтому, чтобы исправить это, вам нужно будет потребовать.

требуют «active_support / debrecation»

require 'active_support / core_ext / array / wrap'


Array(whatever) должен сделать трюк

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

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

Если вы не используете 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

Самое простое решение - использовать [foo].flatten(1) . В отличие от других предлагаемых решений, он будет хорошо работать для (вложенных) массивов, хэшей и 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'}]

Как насчет

[].push(anything).flatten






arrays