Array-ifying Values

I can’t remember if I’ve written about this before. On the assumption that I haven’t, I’ll forge ahead.

As I’ve mentioned in the past I hate null checks. They clutter up code and add nothing meaningful to the code’s narrative. I hate checks to see if a value is singular or plural too. I guess what I’m saying is that I hate type checking and avoid it wherever possible.

Here’s a contrived example of some code that does both a null check and a cardinality check. It takes an argument and produces some HTML list item tags:

def make_list(items)
  case items
  when Array then items.map{|item| html "
#{item}
" }.join
  when nil then ""
  else "
#{items}
" # singular value
  end
end

Blech. Thankfully there is an elegant idiom which cleans this code up nicely:

def make_list(items)
  Array(items).map{|item| "
#{item}
" }.join
end

Kernel.Array() is the “Array-ifier” method. It takes a value and always returns an array. And it’s pretty smart about how it translates:

Array("foo") # => ["foo"]
Array(nil) # => []
Array([1,2,3]) # => [1,2,3]
Array(4..6) # => [4,5,6]
Array({:a => 1, :b => 2}) # => [[:b, 2], [:a, 1]]

As you can see, whatever you give it, you always get an Array back. Note in particular that if it is passed an Array, it returns the argument unaltered rather than wrapping it in another layer of array.

Under the covers, Array() tries to call #to_ary on the passed object, and if that fails it tries #to_a.

If you’ve been using Ruby for a while you might object “but isn’t that what #to_a is for?”. The problem with #to_a is that it’s old default behavior, which was to wrap a non-Array object in an Array, is deprecated. Array() is the future-proof way to array-ify arbitrary objects.

Now go forth, and write self-confident code!

6 comments

    1. They differ in the nil case:

      >> RUBY_DESCRIPTION # => “ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-linux]”
      >> [*nil] # => [nil]
      >> Array(nil) # => []

  1. One thing I don't like about the behavior with Hash transformed to Array is that you can't use it to “normalize” argument to an array (for example to get an API which allows passing both array of hashes and singe hash) so you have to resort to something like [arg].flatten.

  2. One thing I don't like about the behavior with Hash transformed to Array is that you can't use it to “normalize” argument to an array (for example to get an API which allows passing both array of hashes and singe hash) so you have to resort to something like [arg].flatten.

Comments are closed.