from_sentence : the opposite of Rails to_sentence

I like the Rails to_sentence method on String class, which converts an array to a comma-separated sentence where the last element is joined by a connector word.

It makes it easy to take a list of names, for example, and make them human-readable, without having to fiddle with join words and last-item-is-special-case stuff:

['one', 'two'].to_sentence          # => "one and two"
['one', 'two', 'three'].to_sentence # => "one, two, and three"

And you can supply your own join words and final join phrase for the various combinations (2 words, more than 2 words, 3 or more words, etc).

But what if you want to go the other way, and convert a sentence back into an array? There doesn’t seem to be an equivalent “from_sentence” method. (* see disclaimer)

So here’s what it might look like:

class String
  def from_sentence(words_connector: ',', two_words_connector: ' and ', last_word_connector: ', and ')
    self.gsub(/#{last_word_connector}/,"#{words_connector}").gsub(/#{two_words_connector}/,"#{words_connector}").split("#{words_connector}").map(&:strip)
  end
end

The parameters and their defaults are the same as those for the to_sentence method (apart from the locale parameter, which I haven’t got round to).

The code replaces the last word connector and the two word connector with the simple word connector, then splits the string on that connector, and trims the leading or trailing whitespace from the resultant array items.

And some tests might look like this (in an RSpec style):

require 'ext/string'

describe String do

  it "should convert sentence to array with default connectors" do
    "one".from_sentence.should eql(["one"])
    "one and two".from_sentence.should eql(["one","two"])
    "one, two and three".from_sentence.should eql(["one","two","three"])
    "one, two, three, and four".from_sentence.should eql(["one","two","three","four"])
  end

  it "should convert sentence to array with specified connectors" do
    options = {words_connector: '-', two_words_connector: ' -- ', last_word_connector: ' --- '}
    ["one"].to_sentence(options).from_sentence(options).should eql(["one"])
    ["one", "two"].to_sentence(options).from_sentence(options).should eql(["one","two"])
    ["one", "two", "three"].to_sentence(options).from_sentence(options).should eql(["one","two","three"])
    ["one", "two", "three", "four"].to_sentence(options).from_sentence(options).should eql(["one","two","three","four"])
  end

end
  • Disclaimer: In reality, I suspect that if you’re trying to convert human-readable sentences – which should be purely for presentation – back into arrays, then your design has gone wrong somewhere, and you should start by reassessing what you’re doing here..!

Leave a Reply

Your email address will not be published.