Учитывая массив, единственный элемент или nil, получить массив - последние два являются массивом с одним элементом и пустым массивом соответственно.
Я ошибочно подумал, что Ruby будет работать следующим образом:
[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)
Какие приложения у этого есть? Зачем вообще преобразовывать в массив?
В ActiveRecord Rails вызов, скажем, user.posts
будет либо возвращать массив сообщений, либо одну запись, либо ноль. При написании методов, которые работают с результатами этого, проще всего предположить, что метод будет принимать массив, который может иметь ноль, один или несколько элементов. Пример метода:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
user.posts
никогда не должен возвращать ни одного сообщения. По крайней мере, я этого не видел.==
а не=
, не так ли?[1,2,3].to_a
вовсе не вернется[[1,2,3]]
! Он возвращается[1,2,3]
.Ответы:
[*foo]
илиArray(foo)
будет работать большую часть времени, но в некоторых случаях, например с хешем, это портит.Единственный способ, который, как я могу представить, работает даже для хеша, - это определить метод.
источник
ensure_array
продленияto_a
to_a
. Например,{a: 1, b: 2}.each ...
работал бы иначе.С ActiveSupport (Rails):
Array.wrap
Если вы не используете Rails, вы можете определить свой собственный метод, аналогичный исходному тексту rails .
источник
class Array; singleton_class.send(:alias_method, :hug, :wrap); end
для дополнительной привлекательности.Самое простое решение - использовать
[foo].flatten(1)
. В отличие от других предлагаемых решений, он будет хорошо работать для (вложенных) массивов, хэшей иnil
:источник
Kernel#Array
т.е.Array()
самый быстрый из них. Сравнение Ruby 2.5.1: Array (): 7936825.7 i / s. Array.wrap: 4199036,2 i / s - в 1,89 раза медленнее. wrap: 644030,4 i / s - в 12,32 раза медленнееArray(whatever)
должен сделать трюкисточник
ActiveSupport (Rails)
У ActiveSupport есть для этого довольно хороший метод. Он загружен с Rails, так что лучший способ сделать это:
Сплат (Ruby 1.9+)
Оператор splat (
*
) распаковывает массив, если может:Конечно, без массива он делает странные вещи, и объекты, которые вы «расклеиваете», нужно помещать в массивы. Это несколько странно, но это означает:
Если у вас нет ActiveSupport, вы можете определить метод:
Хотя, если вы планируете иметь большие массивы и меньше вещей, не связанных с массивами, вы можете изменить его - вышеуказанный метод работает медленно с большими массивами и может даже вызвать переполнение вашего стека (omg so meta). В любом случае, вы можете сделать это вместо этого:
У меня также есть несколько тестов с оператором teneray и без него.
источник
SystemStackError: stack level too deep
для элементов 1M (ruby 2.2.3).Hash#values_at
1M аргументов (используяsplat
), и он выдает ту же ошибку.object.is_a? Array ? object : [*object]
?Array.wrap(nil)
[]
не возвращаетсяnil
: /Как насчет
источник
[].push(anything).flatten(1)
должно сработать! Он не сглаживает вложенные массивы!Рискуя заявить очевидное и зная, что это не самый вкусный синтаксический сахар, который когда-либо видели на планете и в окрестностях, этот код, кажется, делает именно то, что вы описываете:
источник
вы можете перезаписать метод массива Object
все наследует Object, поэтому to_a теперь будет определено для всего, что находится под солнцем
источник
Я просмотрел все ответы и в основном не работает в ruby 2+
Но у elado есть самое элегантное решение, т.е.
К сожалению, это также не работает для Ruby 2+, так как вы получите сообщение об ошибке
Итак, чтобы исправить это, вам необходимо потребовать.
источник
Поскольку метод
#to_a
уже существует для двух основных проблемных классов (Nil
иHash
), просто определите метод для остальных, расширивObject
:а затем вы можете легко вызвать этот метод для любого объекта:
источник