6
OCT2008
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 passednil
- 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.
Leave a Comment (using GitHub Flavored Markdown)