Victor Feitosa
Victor Feitosa

Reputation: 557

How to make only the first element of a array capitalize in Ruby

I've been given a string. First, I transform it into a array and then I try to capitalize only words into the condition. One of the conditions is that the first string is capitalized. And it's just the one that is left

class Book
# write your code her
attr_reader :title
    def title=(new_title)
        words = new_title.split(" ")
        if words.length > 1     
            final_title = ""
            final_title = words.map! {|a| ((a != "and" && a != "the") && a.length > 2) ? a.capitalize : a}
            @title = final_title.join(" ") 
        else
            @title = new_title.capitalize
        end
    end
end

That's what I've done until now.

I tried to use each_with_index but map! doesn't work with it.

I expected:

"To Kill a Mockingbird"

but I got:

"to Kill a Mockingbird"

Upvotes: 2

Views: 825

Answers (5)

Hamza Satti
Hamza Satti

Reputation: 1

Try This:

string.split(' ').map(&:capitalize).join(' ')

Upvotes: 0

Eric Duminil
Eric Duminil

Reputation: 54233

Here's a much shorter version, using gsub with block :

  • It works with any case as input
  • It checks directly in the regex if the word has at least 3 characters
  • It uses capitalize on new_title : that way, the first word is capitalized and all the others are lowercase before the processing.

Here's the method :

OMIT_CAPITALIZE = %w(the and)
new_title.capitalize.gsub(/\S{3,}/) do |word|
  OMIT_CAPITALIZE.include?(word) ?  word : word.capitalize
end

Here's a way to integrate it into your class. title has been added as a parameter to initialize for easier use of Book :

class Book
  OMIT_CAPITALIZE = %w(the and)

  attr_reader :title

  def initialize(title)
    self.title = title
  end

  def title=(new_title)
    @title = new_title.capitalize.gsub(/\S{3,}/) do |word|
      OMIT_CAPITALIZE.include?(word) ?  word : word.capitalize
    end
  end
end

puts Book.new('to kill a mockingbird').title
# To Kill a Mockingbird
puts Book.new('ThE beauty and THE beast').title
# The Beauty and the Beast
puts Book.new('TO BE OR NOT TO BE').title
# To be or Not to be

Upvotes: 2

moveson
moveson

Reputation: 5213

You can map over an array with an index, but you have to do it like this:

class Book
  attr_reader :title

  def title=(new_title)
    words = new_title.split
    final_title = words.map.with_index { |word, i| primary_word?(word) || i == 0 ? word.capitalize : word }
    @title = final_title.join(' ')
  end

  def primary_word?(word)
    ((word != 'and' && word != 'the') && word.length > 2)
  end
end

For clarity, I also extracted the logic for determining if a word should be capitalized into its own method.

Upvotes: 2

David Stanley
David Stanley

Reputation: 343

Since version 1.9.3, Ruby has had Enumerator#with_index. The method map without a block returns an Enumerator, so you can do the following:

final_title = words.map.with_index do |word, i|
  if i != 0 && (word == "and" || word == "the" || word.length < 3)
    word.downcase
  else
    word.capitalize
  end
end

Clearly, you should make sure your title is lowercase to begin with, or the code checking for "and" and "the" won't work.

Upvotes: 3

Stefan
Stefan

Reputation: 114188

I'd start by separating the first word from the remaining words:

first, *rest = new_title.split(' ')

Then I would capitalize the first word:

first.capitalize!

Afterwards, I would capitalize each remaining word matching the conditions:

rest.select { |w| w != 'and' && w != 'the' && w.length > 2 }.each(&:capitalize!)

And finally put everything back together:

[first, *rest].join(' ')

Upvotes: 8

Related Questions