给定数组,单个元素或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-ish方法,我会很惊讶)
在Rails的ActiveRecord中,调用say,user.posts
将返回一组帖子,一个帖子或零。在编写处理结果的方法时,最简单的假设该方法将采用一个数组,该数组可能包含零个,一个或多个元素。示例方法:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
答案 0 :(得分:138)
[*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}]
答案 1 :(得分:102)
使用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,则可以定义自己的方法,类似于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
答案 2 :(得分:20)
最简单的解决方案是使用[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'}]
答案 3 :(得分:17)
Array(whatever)
应该做的伎俩
Array([1,2,3]) # [1,2,3]
Array(nil) # []
Array(1337) # [1337]
答案 4 :(得分:13)
ActiveSupport有一个非常好的方法。它装载了Rails,所以最好的方法就是这样做:
Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil
splat运算符(*
)取消数组的数组,如果可以的话:
*[1,2,3] #=> 1, 2, 3 (notice how this DOES not have braces)
当然,如果没有阵列,它会做一些奇怪的事情,以及你所做的对象&#34; splat&#34;需要放在数组中。这有点奇怪,但它意味着:
[*[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
虽然,如果你计划拥有大型数组,而不是非数组的东西,你可能想要改变它 - 上面的方法对于大型数组来说很慢,甚至可能导致你的Stack溢出(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运算符。
答案 5 :(得分:6)
怎么样
[].push(anything).flatten
答案 6 :(得分:2)
由于存在陈述明显的风险,并且知道这不是地球和周围地区有史以来最美味的语法糖,这段代码似乎完全符合您的描述:
foo = foo.is_a?(Array) ? foo : foo.nil? ? [] : [foo]
答案 7 :(得分:1)
你可以覆盖Object
的数组方法class Object
def to_a
[self]
end
end
一切都继承了对象,因此现在将为太阳下的所有东西定义to_a
答案 8 :(得分:1)
我已经完成了所有的答案,并且大多数都没有在红宝石2 +中工作
但是elado拥有最优雅的解决方案,即
使用ActiveSupport(Rails):Array.wrap
Array.wrap([1,2,3])#=&gt; [1,2,3]
Array.wrap(1)#=&gt; [1]
Array.wrap(nil)#=&gt; []
Array.wrap({a:1,b:2})#=&gt; [{:a =&gt; 1,:b =&gt; 2}]
可悲的是,这也不适用于ruby 2+,因为你会收到错误
undefined method `wrap' for Array:Class
所以为了解决你需要的问题。
要求&#39; active_support / deprecation&#39;
要求&#39; active_support / core_ext / array / wrap&#39;
答案 9 :(得分:0)
由于方法#to_a
已存在两个主要有问题的类(Nil
和Hash
),因此只需通过扩展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
# => []