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!