Tyler Curtis Jowers
Tyler Curtis Jowers

Reputation: 457

Take a string, capitalize only words with more than three characters unless it is in the beginning

When I enter as an input, "war and peace", I want, "War and Peace". The idea is to take a string and make it appear as it would as a headline or movie title. Capitalize all words in the string, unless the word is equal to or less than 3 and not in the beginning.

When I run this code:

def titleize(string)
      string.split(" ").take(1).join.capitalize
      string.split(" ").map do |x|

        x.capitalize! if x.length > 3

      end.join(" ")
end

It returns only " Peace".

Upvotes: 0

Views: 305

Answers (5)

Cary Swoveland
Cary Swoveland

Reputation: 110665

There is no need to convert the string into an array of words, capitalize words having more than three characters, joining them back into a string and capitalize the first character of the resulting string. Moreover, that would strip extra spaces, which may not be desired.

Instead, one can simply use String#gsub with a regular expression to make the substitutions.

r = /
    \A               # match the start of the string
    \p{Lower}        # match a lowercase letter
    |                # or
    (?<=[ ])         # match a space in a positive lookbehind
    \p{Lower}        # match a lowercase letter
    (?=\p{Alpha}{3}) # match 3 letters in a positive lookahead 
    /x               # free-spacing regex definition mode

str = "now is the    time for something to happen."

str.gsub(r) { |c| c.upcase }
  #=> "Now is the    Time for Something to Happen."

The regex is conventionally written

/\A\p{Lower}|(?<= )\p{Lower}(?=\p{Alpha}{3})/

I used \p{Lower} rather than \p{Alpha} to avoid unnecessary capitalizations.

Note that in free-spacing mode the space in the positive lookbehind is enclosed in a character class ((?<=[ ])). That's because free-spacing mode strips all spaces that are not so-protected.

Upvotes: 2

Eddie
Eddie

Reputation: 496

Try this:

title = 'war and peace'

def titleize(input)
  input.split(' ').map.with_index { |word, index|
    index == 0 || word.length > 3 ? word.capitalize : word
  }.join(' ')
end


titleize title
=> "War and Peace"

Upvotes: 2

Andrew
Andrew

Reputation: 423

Try:

def titleize(string)
  string.split.map.with_index {|x,i| if (i == 0 || x.length > 3) then x.capitalize else x end }.join(' ')
end

Note, this does not modify the string passed in, but you can do the following

foo = 'war and peace'
foo = titleize foo
puts foo
War and Peace

Upvotes: 0

Simple Lime
Simple Lime

Reputation: 11035

The reason you're only returning 'Peace' is because map

Invokes the given block once for each element of self.

Creates a new array containing the values returned by the block.

When you do x.capitalize! if x.length > 3, you return nil if the length <= 3

"war" if "war".length > 3
# => nil

So to make this work, you'll need to make sure you're returning something for each word passed to the block

def titleize(string)
  string.capitalize.split.map do |x|
    x.length > 3 ? x.capitalize : x
  end.join(" ")
end

puts titleize("war and peace") # => "War and Peace"

Also, note that capitalize!

Modifies str by converting the first character to uppercase and the remainder to lowercase. Returns nil if no changes are made.

Whereas capitalize

Returns a copy of str with the first character converted to uppercase and the remainder to lowercase.

so x.capitalize! could also return nil

"Peace".capitalize!
=> nil

Upvotes: 2

Link0352
Link0352

Reputation: 201

I would split the string into an array of strings like so:

string.split(' ')

Then capitalize each the first letter if the length of a given string is greater than 3:

for i in string.length
    if string[i].length > 3 
      string[i].capitalize

Now just join the string:

  string.join(' ')
end

Upvotes: 0

Related Questions