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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18358717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 06:07:47  来源:igfitidea点击:

Ruby - elegantly convert variable to an array if not an array already

rubyarrays

提问by xxjjnn

Given an array, a single element, or nil, obtain an array - the latter two being a single element array and an empty array respectively.

给定一个数组、单个元素或 nil,获得一个数组 - 后两个分别是单个元素数组和空数组。

I mistakenly figured Ruby would work this way:

我错误地认为 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

But what you really get is:

但你真正得到的是:

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

So to solve this, I either need to use another method, or I could meta program by modifying the to_a method of all classes I intend to use - which is not an option for me.

所以为了解决这个问题,我要么需要使用另一种方法,要么我可以通过修改我打算使用的所有类的 to_a 方法来元编程 - 这对我来说不是一个选择。

So a Method it is:

所以一个方法是:

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

The problem is that it is a bit of a mess. Is there an elegant way of doing this? (I would be amazed if this is the Ruby-ish way to solve this problem)

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



What applications does this have? Why even convert to an array?

这有什么应用?为什么还要转换成数组?

In Rails' ActiveRecord, calling say, user.postswill either return an array of posts, a single post, or nil. When writing methods which work on the results of this, it is easiest to assume that the method will take an array, which may have zero, one, or many elements. Example method:

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

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

回答by sawa

[*foo]or Array(foo)will work most of the time, but for some cases like a hash, it messes it up.

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

The only way I can think of that works even for a hash is to define a method.

我能想到的唯一方法是定义一个方法,即使对于散列也是如此。

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

回答by elado

With ActiveSupport (Rails): Array.wrap

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

If you are not using Rails, you can define your own method similar to the rails source.

如果您不使用 Rails,您可以定义自己的类似于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

回答by oli

The simplest solution is to use [foo].flatten(1). Unlike other proposed solutions, it will work well for (nested) arrays, hashes and nil:

最简单的解决方案是使用[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'}]

回答by Benjamin Gruenbaum

Array(whatever)should do the trick

Array(whatever)应该做的伎俩

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

回答by Ben Aubin

ActiveSupport (Rails)

ActiveSupport(导轨)

ActiveSupport has a pretty nice method for this. It's loaded with Rails, so defiantly the nicest way to do this:

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

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

Splat (Ruby 1.9+)

Splat(红宝石 1.9+)

The splat operator (*) un-arrays an array, if it can:

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

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

Of course, without an array, it does weird things, and the objects you "splat" need to be put in arrays. It's somewhat weird, but it means:

当然,如果没有数组,它会做一些奇怪的事情,并且您“splat”的对象需要放入数组中。这有点奇怪,但这意味着:

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

If you don't have ActiveSupport, you can define the method:

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

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

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

Although, if you plan on having large arrays, and less non-array things, you might want to change it - the above method is slow with large arrays, and can even cause your Stack to Overflow (omg so meta). Anyways, you might want to do this instead:

虽然,如果您打算拥有大数组,而少一些非数组的东西,您可能想要更改它 - 上述方法对于大数组来说很慢,甚至可能导致您的堆栈溢出(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]

I also have some benchmarkswith and without the teneray operator.

我也有一些带有和不带有 teneray 运算符的基准测试

回答by Bruno Meira

How about

怎么样

[].push(anything).flatten

回答by The Pellmeister

With the risk of stating the obvious, and knowing that this isn't the most tasty syntactic sugar ever seen on the planet and surrounding areas, this code seems to do exactly what you describe:

冒着陈述显而易见的风险,并且知道这不是地球和周边地区有史以来最美味的语法糖,这段代码似乎完全符合您的描述:

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

回答by runub

you can overwrite the array method of Object

你可以覆盖Object的数组方法

class Object
    def to_a
        [self]
    end
end

everything inherits Object, therefore to_a will now be defined for everything under the sun

一切都继承了 Object,因此 to_a 现在将被定义为阳光下的一切

回答by Malware Skiddie

I've gone through all the answers and mostly don't work in ruby 2+

我已经完成了所有的答案,但大多不适用于 ruby​​ 2+

But elado has the most elegant solution i.e

但是 elado 有最优雅的解决方案,即

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

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

Sadly but this also doesn't work for ruby 2+ as you will get an error

遗憾的是,这也不适用于 ruby​​ 2+,因为您会收到错误消息

undefined method `wrap' for Array:Class

So in order to fix that you need to require.

因此,为了解决您需要的问题。

require 'active_support/deprecation'

require 'active_support/core_ext/array/wrap'

需要“active_support/deprecation”

需要'active_support/core_ext/array/wrap'

回答by Shoe

Since the method #to_aalready exists for the two main problematic classes (Niland Hash), just define a method for the rest by extending Object:

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

class Object
    def to_a
        [self]
    end
end

and then you can easily call that method on any object:

然后您可以轻松地在任何对象上调用该方法:

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