I created a method that takes a string value with a fixed length of 9 characters and extracts three values, house
(4 characters), level
(2), door
(3). If the input value has another length than 9 then the method should return nil
.
I found several ways to implement the method, but which implementation should I prefer and why?
def extract(value)
return nil unless value.length == 9
[value[0..3].to_i, value[4..5].to_i, value[6..8].to_i]
end
require 'scanf'
def extract(value)
return nil unless value.length == 9
value.scanf('%4d%2d%3d')
end
def extract(value)
value.match(/^(\d{4})(\d{2})(\d{3})$/).captures.map(&:to_i) rescue nil
end
p extract('123456789')
#=> [1234, 56, 789]
p extract('123456789012')
#=> nil
2 Answers 2
My Thoughts
I haven't profiled any of these, but I'm going to assume speed is not a priority.
I like #2 the best. It is clean and simple, and easy to understand what the code is doing at a glance.
I think #1 is ugly, and the sequential array slicing makes me nervous and seems easy to break. If your number sizes change at some point in the future, this one would be the hardest to modify.
I think #3 is better, but not as clear or concise as #2.
I'd start with:
def extract(str)
str.unpack('A4 A2 A3') if str.size == 9
end
extract('123456789') # => ["1234", "56", "789"]
extract('12345678') # => nil
extract('123456789012') # => nil
unpack
is an unsung hero when it comes to tearing apart strings but it's REALLY powerful. Possibly that power is why people shy away from it. It's designed to tear apart sequences of characters and bytes, even binary data. Building on top of pack
it's easy to test for the length and return nil for strings that are not the proper length.
I'd lean toward scanf
as a second choice as, again, it's very powerful and designed for the purpose you're after, only it's oriented to text.
What I like between the two is that unpack
allows whitespace between the format string entries, making it easier to read, and, as a result, easier to maintain. scanf
strings can be pretty intense when you're dealing with a long string and have to break it into sub-strings.
-
\$\begingroup\$ Thanks. I wasn't aware of the
unpack
method. I like your usage ofif
after the extract expression. \$\endgroup\$sschmeck– sschmeck2016年04月13日 11:32:09 +00:00Commented Apr 13, 2016 at 11:32 -
1\$\begingroup\$ Using
if
is taking advantage of Ruby's automatic return of a nil from a method if nothing is explicitly returned or no assignment occurred. In other words, it's just Ruby's default behavior in action. \$\endgroup\$the Tin Man– the Tin Man2016年04月13日 16:10:49 +00:00Commented Apr 13, 2016 at 16:10