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!
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.
Indeed. I personally feel that Array() is more intent-revealing, but [*] is certainly an alternative.
They differ in the nil case:
>> RUBY_DESCRIPTION # => “ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-linux]”
>> [*nil] # => [nil]
>> Array(nil) # => []
I updated a blog post I did on the subject with links to this post, thanks!
http://blog.teambox.com/mrproper-cleaner-blocks…
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
.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
.