In Progress
Unit 1, Lesson 21
In Progress

String Partition

Video transcript & code

Here's a string representing a file name. It's the kind of filename we might run across in a web application, with two file extensions in a row. The first extension represents the file's ultimate type if HTML, while the second, erb, indicates the template processor which should be run on the file to yield a finished HTML document.


Let's pose a very simple problem: we want to split this string into two parts, the target filename and the template extension. We can probably think of a few ways to do this.

One way would be to split the string on periods. Then we take all but the final components and jam them back together to get the target filename. The template extensions is the final element in the split parts.

parts = "index.html.erb".split(".")
parts[0..-2].join(".")          # => "index.html"
parts.last                      # => "erb"

Another way would be to match against a regular expression. Then we can pull out the filename and template extension from the match groups.

match = /(.*)\.(.*)/.match("index.html.erb")
match[1]                        # => "index.html"
match[2]                        # => "erb"

This works well. But whenever I write a regular expression it forces me to make a mental context switch. And in writing this one, I wasn't sure until I tried it whether the first group would eagerly collect as much of the filename as possible, or if it would stop at the first period. For such a simple problem, these extra few seconds of thought feel excessive, at least to me.

Here's a different way to solve the problem. Ruby's String class has a partition method. It takes a pattern and returns exactly three values: everything before the match, the match itself, and everything after the match. Let's try partitioning on a period, and see what we get.

"index.html.erb".partition(".") # => ["index", ".", "html.erb"]

Well this would be useful if we wanted to split the file's basename from all of its extensions. But that's not what we're after.

Fortunately, like many of the String methods, #partition has a reversed sibling named #rpartition. This method gives us exactly the split we want.

"index.html.erb".rpartition(".") # => ["index.html", ".", "erb"]

We can use Ruby's multiple return value assignment to quickly gather up the information we need in a single line. We use the underscore as a throwaway variable to indicate that we don't care about the middle return value.

file, _, ext = "index.html.erb".rpartition(".")
file                            # => "index.html"
ext                             # => "erb"

This is one of the quickest and simplest ways I know of to achieve this particular string split. It's an easy technique to overlook if you don't know about the existence of the partitioning methods. But now you do know about them, so happy hacking!