Ruby – 优雅地将variables转换为数组,如果不是数组的话

给定一个数组,单个元素或零,获得一个数组 – 后两个分别是一个单一的元素数组和一个空的数组。

我错误地认为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方法来进行元程序 – 这对我来说不是一种select。

所以一个方法是:

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

问题是这是一个混乱。 有没有一个这样做的优雅方式? (如果这是Ruby-ish方法来解决这个问题,我会感到惊讶)


这有什么应用程序? 为什么甚至转换为数组?

在Rails的ActiveRecord中,调用说, user.posts将返回一个post数组,单个post,或零。 在编写处理这个结果的方法时,最简单的方法就是假设这个方法需要一个可能有零个,一个或多个元素的数组。 示例方法:

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

[*foo]Array(foo)将在大多数情况下工作,但是对于某些情况(如散列),会使其混乱。

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

我能想到的唯一办法就是定义一个方法。

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

使用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}] 

Array(whatever)应该做的伎俩

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

最简单的解决scheme是使用[foo].flatten(1) 。 不同于其他提议的解决scheme,它将适用于(嵌套)数组,哈希和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'}] 

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) 

当然,如果没有一个数组,它会出现奇怪的事情,而你所“绘制”的对象需要放在数组中。 这有点奇怪,但这意味着:

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

我也有一些基准和没有teneray操作符。

怎么样

 [].push(anything).flatten 

由于存在明显的风险,并且知道这不是地球上及周边地区所见过的最美味的语法糖,这段代码似乎完全符合您的描述:

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

你可以覆盖Object的数组方法

 class Object def to_a [self] end end 

一切都inheritance了Object,所以to_a现在将被定义为sun的所有东西

我已经经历了所有的答案,并且大多不在Ruby +中工作

但是,埃多拥有最优雅的解决scheme,即

使用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}]

可悲的是,但这也不适用于ruby2+,因为你会得到一个错误

 undefined method `wrap' for Array:Class 

所以为了解决你需要的。

要求“active_support / deprecation”

需要'active_support / core_ext / array / wrap'

由于#to_a方法已经存在于两个主要有问题的类( NilHash )中,所以通过扩展Object来为其余的方法定义一个方法:

 class Object def to_a [self] end end 

然后你可以很容易地在任何对象上调用该方法:

 "Hello world".to_a # => ["Hello world"] 123.to_a # => [123] {a:1, b:2}.to_a # => [[:a, 1], [:b, 2]] nil.to_a # => []