0

Ok. It's late and I'm tired.

I want to match a character in a string. Specifically, the appearance of 'a'. As in "one and a half".

If I have a string which is all lowercase.

"one and a half is always good" # what a dumb example. No idea how I thought of that.

and I call titleize on it

"one and a half is always good".titleize #=> "One And A Half Is Always Good"

This is wrong because the 'And' and the 'A' should be lowercase. Obviously.

So, I can do

"One and a Half Is always Good".titleize.tr('And', 'and') #=> "One and a Half Is always Good"

My question: how do I make the "A" an "a" and without making the "Always" into "always"?

2
  • \ba\b The \b matches a word boundary. You are looking for the regex for that, right? Commented Dec 18, 2010 at 7:10
  • "One and A Half Is Always Good".gsub(/\bA\b/, 'a') # => "One and a Half Is Always Good" Commented Dec 18, 2010 at 8:11

2 Answers 2

2

This does it:

require 'active_support/all'
str = "one and a half is always good" #=> "one and a half is always good"

str.titleize.gsub(%r{\b(A|And|Is)\b}i){ |w| w.downcase } #=> "One and a Half is Always Good"

or

str.titleize.gsub(%r{\b(A(nd)?|Is)\b}i){ |w| w.downcase } #=> "One and a Half is Always Good"

Take your pick of either of the last two lines. The regex pattern could be created elsewhere and passed in as a variable, for maintenance or code cleanliness.

Sign up to request clarification or add additional context in comments.

Comments

2

I like Greg's two-liner (first titleize, then use a regex to downcase selected words.) FWIW, here's a function I use in my projects. Well tested, although much more verbose. You'll note that I'm overriding titleize in ActiveSupport:

class String
  #
  # A better titleize that creates a usable
  # title according to English grammar rules.
  #
  def titleize
    count  = 0
    result = []

    for w in self.downcase.split
      count += 1
      if count == 1
        # Always capitalize the first word.
        result << w.capitalize
      else
        unless ['a','an','and','by','for','in','is','of','not','on','or','over','the','to','under'].include? w
          result << w.capitalize
        else
          result << w
        end
      end
    end

    return result.join(' ')
  end
end

3 Comments

String has a titleize? You mean you're overriding Active_Support's titleize.
FWIW, it might be a good idea to convert your array of strings to a Hash for lookup. Calling Array#include? once for each word in each string to be titleized seems inefficient. stopwords = Hash[ %w[ a an and by for ... ].map{ |w| [w,true] }.flatten ] ... unless stopwords[w] ...
Thanks for the help. this is a good solution but, you're right, Greg's is a little easier. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.