Working Remotely? Thinking about it?

Are you a member of a geographically dispersed team? Are you thinking of working remotely, or hiring remote developers? Wide Teams is my new blog and podcast for distributed teams. Check it out for getting started guides, tips and best practices, news, interviews, screencasts, and more all about working remotely and collaborating with wide-spread teams.

Array-ifying Values

2009 October 21
by avdi

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!

    Bookmark and Share
    Creative Commons License
    This work, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.

    Related posts:

    1. Writing Self-Confident Code
    2. First and Rest in Ruby
    3. Hash Transforms in Ruby
    4. [ANN] fail-fast 1.0.0 Released
    5. Go Fetch
    • 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`.
    • I updated a blog post I did on the subject with links to this post, thanks!

      http://blog.teambox.com/mrproper-cleaner-blocks-in-ruby
    • 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.
    • They differ in the nil case:

      >> RUBY_DESCRIPTION # => "ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-linux]"
      >> [*nil] # => [nil]
      >> Array(nil) # => []
    • Indeed. I personally feel that Array() is more intent-revealing, but [*] is certainly an alternative.
    blog comments powered by Disqus