Ruby Voodoo

Deep dives into random corners of my favorite programming language.

6

OCT
2008

Conversion Methods

I want to take a step back from all the syntax I've been covering lately and just talk about some simple methods in Ruby's core. Ruby ships with so many great helpers, it's often hard to keep track of what everything can do. Specifically, let's talk about the type conversion methods.

I assume we all make calls to to_s() and to_i() regularly:

255.to_s    # => "255"
"255".to_i  # => 255

There shouldn't be any surprises there. Even these two simple methods can do more though. They make it possible to convert to and from various numeric bases. For example, here are the same conversions into and out of base 16 (hexadecimal):

255.to_s(16)   # => "ff"
"ff".to_i(16)  # => 255

Ruby has other ways to do these same conversions. Here are two unusual methods (beginning with capital letters) that are similar:

String(255)     # => "255"
Integer("255")  # => 255

I'll be honest and tell you that I don't really find String() useful as it just calls to_s() for you, but Integer() is a different story. First of all, to_i() is very lenient about what it converts while Integer() is more strict:

p "C#".to_i
# >> 0
Integer("C#")
# ~> -:5:in `Integer': invalid value for Integer: "C#" (ArgumentError)

That can be handy when you want to be sure you you have a number.

Now while Integer() must be passed a number, it understands numbers in all of the native formats Ruby does: decimal, octal, hexadecimal, and binary. In comparison, to_i() just understands decimal integers:

255                    # => 255
0377                   # => 255
0xFF                   # => 255
0b11111111             # => 255

Integer("255")         # => 255
Integer("0377")        # => 255
Integer("0xFF")        # => 255
Integer("0b11111111")  # => 255

"255".to_i             # => 255
"0377".to_i            # => 377
"0xFF".to_i            # => 0
"0b11111111".to_i      # => 0

If you want to be sure you are passing in safe content to either method, you should probably verify the String contents with a regular expression before you make the call.

There is a Float() method as well, but it doesn't give you much over to_f(). The only significant difference is how they handle nil:

p nil.to_f
# >> 0.0
Float(nil)
# ~> -:10:in `Float': can't convert nil into Float (TypeError)

Numbers aren't the only thing we can convert in Ruby though. Let's talk about Array(). Ruby use to have a direct to_a() method on all objects, but it has been deprecated in Ruby 1.8:

$ ruby -ve 'p 5.to_a'
ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-darwin9.4.0]
-e:1: warning: default `to_a' will be obsolete
[5]

And it's finally gone in Ruby 1.9:

$ ruby_dev -ve 'p 5.to_a'
ruby 1.9.0 (2008-09-27 revision 0) [i386-darwin9.5.0]
-e:1:in `<main>': undefined method `to_a' for 5:Fixnum (NoMethodError)

That leaves us with just Array(), but that's not a bad thing at all since it is very handy. What makes Array() great is the three different behaviors it has on the objects you pass:

  • It has no effect on an Array which is return unchanged
  • It returns an empty Array when passed nil
  • All other objects are returned wrapped in an Array

Here are examples of each of those:

Array([1, 2, 3])  # => [1, 2, 3]
Array(nil)        # => []
Array(5)          # => [5]

These simple rules mean that you can count on an Array being returned from Array(), no matter what you pass it. Thus it is safe to call any Array methods on the result, including iterators.

To give a more practical example, let's say you have some parameter your Rails application is prepared to receive. You may receive any number of items in this parameter: zero, one, or many. You can process them with a simple:

Array(params[:choices]).each do |choice|
  # process choice here...
end

There's one last conversion method that I make regular use of and it is Hash[]. Note the brackets there as they aren't parentheses like the other methods we've been talking about. This one is a class method on Hash.

This method is very simple in function. It takes an even number of arguments and creates a Hash:

Hash["a", 1, "b", 2]  # => {"a"=>1, "b"=>2}

At first glance, that doesn't seem to gain us much over the normal Hash literal syntax of { … }. However, this is a method call we can use to build a Hash and that means we can apply the normal tricks of method calls to it, like splatting an Array of arguments:

Hash[*%w[a 1 b 2]]  # => {"a"=>"1", "b"=>"2"}

That opens up all kinds of possibilities for converting from Array objects to Hash objects or for building iterations that result in a Hash:

h = Hash[*%w[a 1 b 2 c 3].map { |f| Integer(f) rescue f }]
p h
# >> {"a"=>1, "b"=>2, "c"=>3}
p Hash[*h.select { |_, n| (n % 2).nonzero? }.flatten]
# >> {"a"=>1, "c"=>3}

Notice how we get a Hash out of the last line there when Ruby 1.8 would usually give us an Array of Array objects. Ruby 1.9 modifies most Hash iterators to return a Hash though:

$ ruby_dev -ve 'p({"a"=>1, "b"=>2, "c"=>3}.select { |_, n| (n % 2).nonzero? })'
ruby 1.9.0 (2008-09-27 revision 0) [i386-darwin9.5.0]
{"a"=>1, "c"=>3}

Hopefully that gives you some fresh ideas about how you might handle simple conversions in the future.

In: Ruby Voodoo | Tags: APIs | 0 Comments
Comments (0)
Leave a Comment (using GitHub Flavored Markdown)

Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

Ajax loader