Ruby - 如果不是数组,则优雅地将变量转换为数组

xxj*_*jnn 111 ruby arrays

给定一个数组,单个元素或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
Run Code Online (Sandbox Code Playgroud)

但你真正得到的是:

[1,2,3].to_a  #= [1,2,3]         # Hooray
1.to_a        #= NoMethodError   # Do not want
nil.to_a      #= []              # Hooray
Run Code Online (Sandbox Code Playgroud)

所以要解决这个问题,我需要使用另一种方法,或者我可以通过修改我打算使用的所有类的to_a方法来进行元编程 - 这对我来说不是一个选项.

所以它是一个方法:

result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))
Run Code Online (Sandbox Code Playgroud)

问题是它有点混乱.这样做有一种优雅的方式吗?(如果这是解决这个问题的Ruby-ish方法,我会很惊讶)


这有什么应用?为什么甚至转换为阵列?

在Rails的ActiveRecord中,调用say user.posts会返回一组帖子,一个帖子或者nil.在编写处理结果的方法时,最简单的假设该方法将采用一个数组,该数组可能包含零个,一个或多个元素.示例方法:

current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
Run Code Online (Sandbox Code Playgroud)

saw*_*awa 144

[*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]]
Run Code Online (Sandbox Code Playgroud)

我能想到的唯一方法就是哈希就是定义一个方法.

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}]
Run Code Online (Sandbox Code Playgroud)

  • @screenmutt这会影响依赖于`to_a`的原始使用的方法.例如,`{a:1,b:2} .each ......`的工作方式不同. (8认同)
  • 而不是`ensure_array`,扩展`to_a` (2认同)
  • @mambo在我发布问题后的某个时刻,我确实找到了答案.困难的部分是它与Array类无关,但它是内核模块上的一个方法.http://ruby-doc.org/core-2.3.1/Kernel.html#method-i-Array (2认同)

ela*_*ado 109

使用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}]
Run Code Online (Sandbox Code Playgroud)

如果您不使用Rails,则可以定义类似于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
Run Code Online (Sandbox Code Playgroud)

  • `class Array; singleton_class.send(:alias_method,:hug,:wrap); 为了额外的可爱而结束. (9认同)

oli*_*ito 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'}]
Run Code Online (Sandbox Code Playgroud)


Ben*_*aum 17

Array(whatever) 应该做的伎俩

Array([1,2,3]) # [1,2,3]
Array(nil) # []
Array(1337)   # [1337]
Run Code Online (Sandbox Code Playgroud)

  • 不适用于哈希.数组({a:1,b:2})将是[[:a,1],[:b,2]] (14认同)

Ben*_*bin 13

ActiveSupport(Rails)

ActiveSupport有一个非常好的方法.它装载了Rails,所以最好的方法是:

Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil
Run Code Online (Sandbox Code Playgroud)

Splat(Ruby 1.9+)

splat运算符(*)取消数组的数组,如果它可以:

*[1,2,3] #=> 1, 2, 3 (notice how this DOES not have braces)
Run Code Online (Sandbox Code Playgroud)

当然,没有数组,它会做奇怪的事情,你"splat"的对象需要放在数组中.这有点奇怪,但它意味着:

[*[1,2,3]] #=> [1, 2, 3]
[*5] #=> [5]
[*nil] #=> []
[*{meh: "meh"}] #=> [[:meh, "meh"], [:meh2, "lol"]]
Run Code Online (Sandbox Code Playgroud)

如果您没有ActiveSupport,则可以定义方法:

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

Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil
Run Code Online (Sandbox Code Playgroud)

虽然,如果你计划拥有大型数组,而不是非数组的东西,你可能想要改变它 - 上面的方法对于大型数组来说很慢,甚至可能导致你的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]
Run Code Online (Sandbox Code Playgroud)

我也有一些有或没有teneray操作员的基准测试.


小智 6

怎么样

[].push(anything).flatten
Run Code Online (Sandbox Code Playgroud)

  • 是的,我认为我最终在我的情况下使用[任何] .flatten ...但是对于一般情况,这也会使任何嵌套的数组结构变平 (2认同)
  • `[].push(anything).flatten(1)` 会起作用!它不会展平嵌套数组! (2认同)