4

最近我偶然发现了“ Ruby #3 中的风格元素:确保某物是一个数组”。

TL;DR:Array()Ruby 中的方法将您放入的任何内容转换为数组,并尝试猜测正确的结果应该是什么:

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

那么Array({a: :b})返回什么?我假设它返回一个以哈希为值的数组:[{a: :b}]

但是,哈希直接转换为数组:[:a, :b]

我不只是想将哈希放入数组([{a: :b}])中。无论我放入什么,我都希望具有返回数组的功能。Array()已经这样做了,但是它以我不期望的方式将哈希转换为数组。

所以,基本上功能应该是这样的:

NewFancyArrayMethod({a: :b}) # => [{a: :b}]
NewFancyArrayMethod([{a: :b}, {c: :d}]) # => [{a: :b}, {c: :d}]

第二部分已经由 完成Array()

我知道我可以做类似的事情values = [values] unless values.is_a? Array,就像文章指出的那样。但是,我宁愿有一种方法可以像Array()已经做的那样从我那里抽象出这种转换。唯一的问题是Array()对哈希的处理不同于任何其他“单个”值(字符串、整数、对象等)。我只是不希望对不同的情况进行不同的处理。

4

4 回答 4

8

那么Array({a: :b})返回什么?我假设它返回一个以哈希为值的数组:[{a: :b}]

你的假设是错误的。通过尝试(按此顺序)Kernel#Array转换参数:arg

  1. arg.to_ary
  2. arg.to_a
  3. 最后创建[arg]

例子:

Array(1)        #=> [1]

这是因为(3)。没有Fixnum#to_aryFixnum#to_a

Array([1, 2])   #=> [1, 2]

[[1, 2]]由于(1),这不会返回。Array#to_ary返回self

Array(nil)      #=> []

[nil]由于(2),这不会返回。没有NilClass#to_ary,但有NilClass#to_a“总是返回一个空数组。”

Array({a: :b})  #=> [[:a, :b]]

由于(2),这样的Array(nil)情况不会返回。[{a: :b}]没有Hash#to_ary,但有Hash#to_a“将 hsh 转换为 [key, value] 数组的嵌套数组。”

Array(Time.now) #=> [33, 42, 17, 22, 8, 2013, 4, 234, true, "CEST"]

这正在Time#to_a返回...... “时间值的十元素数组”

结论:

Kernel#Array按预期工作,Hash#to_a是罪魁祸首。(或to_a一般而言)

我想要一个功能,无论我输入什么,它都会返回一个数组。

Hash并且Array是不同的对象。您可以检查类型(Kernel#Array也这样做):

def hash_to_array(h)
  h.is_a?(Array) ? h : [h]
end

或扩展Hash(甚至Object)和Array

class Hash # or Object
  def my_to_a
    [self]
  end
end

class Array
  def my_to_a
    self
  end
end

有关替代实现,请参阅注释。

于 2013-08-22T14:25:47.780 回答
3

正如Frederick Cheung在表扬中指出的那样,Array#wrap方法完全符合我的要求:

Array.wrap({a: :b})
# => [{a: :b}]

不幸的是,这个方法只是 ActiveSupport 的一部分,所以它不能用于普通的 Ruby 项目。

于 2014-05-20T08:45:54.047 回答
1

如果您不知道它是一个数组还是您正在使用的哈希,您可以使用它flatten来防止在后者的情况下以嵌套数组结束。像这样:

[foo].flatten

# E.g.
foo = [{:a => :b}, {:c => :d}]
[foo] #=> [[{:a => :b}, {:c => :d}]] -- Nested, call flatten to fix that
[foo].flatten #=> [{:a => :b}, {:c => :d}]

foo = {:a => :b}
[foo] #=> [{:a => :b}] -- Not nested, call flatten anyway just in case
[foo].flatten #=> [{:a => :b}]

这也适用于字符串、整数和对象:

foo = 'bar'
[foo].flatten #=> ['bar']

foo = 1
[foo].flatten #=> [1]

foo = Object.new
[foo].flatten #=> [#<Object:0x00000100a8daf8>]

在任何情况下,你都会得到你想要的。

于 2013-08-22T13:37:20.907 回答
1

然后做猴子补丁如下:

class Hash
  def fancyarraymethod
    [self]
  end
end
class Array
  def fancyarraymethod
    self
  end
end

{a: :b}.fancyarraymethod # => [{a: :b}]
[{a: :b}, {c: :d}].fancyarraymethod # => [{a: :b}, {c: :d}]
于 2013-08-22T13:26:57.590 回答