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!

    This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.
    • http://twitter.com/bradly Bradly Feeley

      You can use [*items] as well to do the same thing as Array(). I don't know if there are any reasons to use one over the other, but it's an option.

      • http://avdi.org avdi

        Indeed. I personally feel that Array() is more intent-revealing, but [*] is certainly an alternative.

      • http://beginrescue.blogspot.com phiggy

        They differ in the nil case:

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

    • http://www.teambox.com Pablo Villalba

      I updated a blog post I did on the subject with links to this post, thanks!

      http://blog.teambox.com/mrproper-cleaner-blocks

    • http://dolzhenko.org Evgeniy Dolzhenko

      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`.

    • http://dolzhenko.org Evgeniy Dolzhenko

      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`.