Kernel#Array
Array(object) -> array Kernel#Array is the Kernel-provided functional converter that turns a value into an Array. It follows the same pattern as String(), Integer(), and Float(): a private method on Kernel that you call as a bare function. The interesting part is the dispatch order, because that is what makes hashes turn into pairs, sets unwrap into elements, and strings wrap into a single-element array.
Signature
The signature:
Array(object) -> array
The method takes exactly one argument. It has no exception: keyword (unlike Integer() and Float()) and no block form. Calling it with zero or more than one argument raises ArgumentError:
Array() # => raises ArgumentError: wrong number of arguments (given 0, expected 1)
Array(1, 2) # => raises ArgumentError: wrong number of arguments (given 2, expected 1)
Because Array is a private instance method on Kernel exposed as a module function, you call it bare (Array([1])) rather than on a receiver.
What it does
Array() walks a fixed dispatch to convert its argument:
- If the argument is
nil, return[]immediately. The methods are never checked. - Otherwise, if the argument responds to
to_ary, call it. The return value must be anArray, orTypeErroris raised. - Otherwise, if the argument responds to
to_a, call it. The return value must be anArray, orTypeErroris raised. - Otherwise, wrap the argument in a one-element array:
[argument].
The to_ary step is the “implicit” conversion hook (the object saying “substitute me transparently”), and to_a is the “explicit” conversion hook (“I can produce an array on request”). Array() honors that order, which is the source of most of the surprises covered in the gotchas below. In MRI, the whole thing runs through the C function rb_f_array, which is a thin wrapper over rb_Array(arg).
Examples
Arrays and ranges
Array already implements to_ary (returning self), so arrays pass through unchanged. Ranges get unwrapped through Enumerable#to_a:
Array(nil) # => []
Array([1, 2, 3]) # => [1, 2, 3]
Array([]) # => []
Array(1..3) # => [1, 2, 3]
Array("a".."c") # => ["a", "b", "c"]
Array() does not flatten. Array([1, [2, 3]]) is [1, [2, 3]]. Reach for Array(x).flatten when you need that.
Hashes and enumerables
Hash defines to_a, which returns an array of [key, value] pairs. That is what Array() ends up calling:
Array({ a: 1, b: 2 }) # => [[:a, 1], [:b, 2]]
Array({}) # => []
Array(Hash.new(0)) # => []
Other Enumerable sources behave the same way. Set is the exception: it defines to_ary (returning the elements), so it unwraps cleanly without going through the pair step:
require "set"
Array(Set[1, 2, 3]) # => [1, 2, 3] to_ary branch
Array(1.upto(3)) # => [1, 2, 3] to_a branch
Wrapping non-array values
Anything that defines neither to_ary nor to_a ends up in a one-element array. That is why Array("hello") is ["hello"], not ["h", "e", "l", "l", "o"]:
Array("hello") # => ["hello"]
Array(42) # => [42]
Array(3.14) # => [3.14]
Array(:ok) # => [:ok]
Array(true) # => [true]
Array(false) # => [false]
Array(/rx/) # => [/rx/]
Array(Time.now) # => [current Time instance]
To split a string into characters, use "abc".chars or "abc".split("").
The to_ary and to_a branches
If you implement one of the two conversion hooks, Array() will pick it up. A non-Array return from either hook is a TypeError:
class Box
def to_ary
[1, 2, 3]
end
end
Array(Box.new) # => [1, 2, 3] to_ary wins
class Bad
def to_ary
"not an array"
end
end
Array(Bad.new)
# => raises TypeError: can't convert Bad into Array (Bad#to_ary gives String)
The same contract holds for to_a, the explicit conversion hook used by Range, Hash, and most Enumerable subclasses when Array() reaches them. A to_a that returns anything other than an Array is still a TypeError, and the message names the offending class and method so you can find the bad implementation fast:
class Pair
def to_a
[:a, :b]
end
end
Array(Pair.new) # => [:a, :b] no to_ary, falls through to to_a
class BadA
def to_a
42
end
end
Array(BadA.new)
# => raises TypeError: can't convert BadA into Array (BadA#to_a gives Integer)
Array() vs to_a vs Array.wrap
Three ways to coerce a value to an array, and the choice matters for some inputs:
| Input | Array() | Object#to_a (if defined) | Array.wrap (Rails) |
|---|---|---|---|
nil | [] | [] (NilClass#to_a) | [] |
[1, 2, 3] | [1, 2, 3] (via to_ary) | [1, 2, 3] | [1, 2, 3] |
1..3 | [1, 2, 3] (via to_a) | NoMethodError on Range | [1, 2, 3] |
{a: 1} | [[:a, 1]] (via to_a) | [[:a, 1]] | [[:a, 1]] |
"abc" | ["abc"] | NoMethodError on String | ["abc"] |
42 | [42] | NoMethodError | [42] |
Set[1, 2] | [1, 2] (via to_ary) | [1, 2] | [1, 2] |
Array() is the strict Kernel conversion that walks the standard protocol. Array.wrap (Rails) is a defensive helper that skips the to_ary step in some edge cases (notably when to_ary returns nil instead of an array) and is the safer pick inside Rails code.
Gotchas
Behaviors that catch people out at least once. The first three are the everyday ones; the rest are protocol and edge cases.
The everyday traps
Array(nil)is[], not[nil]. Useful in defensive code (Array(opts[:tags])); also the single most common “wait, what?” moment.Array("abc")is["abc"], not["a", "b", "c"]. Strings implement neitherto_anorto_ary. Use"abc".charsor"abc".split("")to split into characters.Array({a: 1})is[[:a, 1]]. Hashes implementto_a(returning pairs), notto_ary. If you wanted the keys, writeArray(hash.keys).
Protocol and edge cases
- Open ranges do not work.
Array(1..Float::INFINITY)raises because the range is not enumerable to a finite array. Use(1..n).to_aor1.upto(n).to_afor explicit intent. - A non-
Arrayreturn fromto_aryorto_ais aTypeError.Array()is a strict caller of the conversion protocol, so a buggyto_aryin a third-party class will surface here. - Sets are unwrapped, hashes are pairified.
Set[1, 2, 3]definesto_aryand returns[1, 2, 3].{a: 1}definesto_aand returns[[:a, 1]]. Different conversion contracts, worth keeping straight. - Frozen inputs are not duplicated. If you pass a frozen array, you get the same object back, not a copy.
Array([1, 2, 3].freeze).frozen?istrue. Calldupfirst if you need a fresh mutable array. - No
exception:keyword. UnlikeInteger(),Float(),Complex(), andRational(), there is no opt-in for “returnnilon failure.” The standard workaround isArray(obj) rescue nil. Array()is private. You call it as a bare function. You cannot call it on an explicit receiver in the usual way, which is whyArray()is so handy in DSLs andmodule_evalblocks.Array()does not recurse. It does not flatten nested arrays. UseArray(x).flattenorArray(x).flatten(1)for that.
Patterns in real code
A few small patterns that show up in production Ruby code. The first is the one most worth keeping in your head: Array() is the safe Ruby coercion for “give me an array of this, with nil handled”:
def tag_list(opts)
Array(opts[:tags]) # nil -> [], Array -> itself, String -> [string], Range -> elements
end
def as_pairs(maybe_hash)
Array(maybe_hash) # nil -> [], Hash -> [[k, v], ...], other -> [other]
end
def flatten_input(maybe_nested)
Array(maybe_nested).flatten # never raises; flattens recursively
end
Two reminders about the conversion protocol that bit people the first time. Set defines to-ary and unwraps to its elements. Hash defines to_a and pairifies to [[k, v], ...]. The contracts are not symmetric, so pick the one that matches the input shape you actually expect:
require "set"
Array(Set[1, 2, 3]) # => [1, 2, 3] via to-ary, returns the elements
Array({ a: 1, b: 2 }) # => [[:a, 1], [:b, 2]] via to_a, returns pairs
If you want the defensive “give me a one-level array of this, period” behavior in Rails, prefer Array.wrap over Array(); it skips the to-ary step when to-ary returns nil and is the safer pick inside Rails code. Outside Rails, Array(obj) rescue nil is the standard idiom for the rare case where you want coercion to fail soft.
See also
- Array class — the
Arrayclass itself, the destination of the conversion, not the function. - Kernel#String — sibling converter
String(), with the same private-method-as-function shape. - Kernel#Float — sibling converter
Float(), the contrast for theexception:keyword story.