Ruby 将字符串转换为正确的标题大小写

Ruby 将字符串转换为正确的标题大小写,ruby,string,title-case,Ruby,String,Title Case,我有以下练习: 编写一个用字符串初始化的标题类 它有一个方法--fix--它应该返回字符串的标题大小写版本: Title.new(“书名”).fix= 书名 您需要使用条件逻辑--if和else语句来实现此功能。 确保仔细阅读测试规范,以便理解要实现的条件逻辑 您将要使用的一些方法: String#downcase String#大写 Array#include? 此外,这是Rspec,我应该包括: describe "Title" do describe "fix" do it "capita

我有以下练习:

编写一个用字符串初始化的
标题

它有一个方法--
fix
--它应该返回字符串的标题大小写版本:

Title.new(“书名”).fix
= 书名
您需要使用条件逻辑--
if
else
语句来实现此功能。
确保仔细阅读测试规范,以便理解要实现的条件逻辑

您将要使用的一些方法:

String#downcase
String#大写
Array#include?

此外,这是Rspec,我应该包括:

describe "Title" do
describe "fix" do
it "capitalizes the first letter of each word" do
  expect( Title.new("the great gatsby").fix ).to eq("The Great Gatsby")
end
it "works for words with mixed cases" do
  expect( Title.new("liTTle reD Riding hOOD").fix ).to eq("Little Red Riding Hood")
end
it "downcases articles" do
  expect( Title.new("The lord of the rings").fix ).to eq("The Lord of the Rings")
  expect( Title.new("The sword And The stone").fix ).to eq("The Sword and the Stone")
  expect( Title.new("the portrait of a lady").fix ).to eq("The Portrait of a Lady")
end
it "works for strings with all uppercase characters" do
  expect( Title.new("THE SWORD AND THE STONE").fix ).to eq("The Sword and the Stone")
end
end
end
谢谢@simone,我采纳了您的建议:

class Title
attr_accessor :string

def initialize(string)
@string = string
end

IGNORE = %w(the of a and)

def fix
s = string.split(' ')
s.map do |word|
  words = word.downcase
  if IGNORE.include?(word)
    words
  else
    words.capitalize
  end
end
s.join(' ')
end
end
尽管我在运行代码时仍遇到错误:

expected: "The Great Gatsby"
 got: "the great gatsby"

(compared using ==)

exercise_spec.rb:6:in `block (3 levels) in <top (required)>' 
期待:《了不起的盖茨比》
得到:“伟大的盖茨比”
(使用==进行比较)
练习_规范rb:6:in‘分块(3层)in’
从初学者的角度来看,我看不出我做错了什么

最后的编辑:我只是想说谢谢大家之前为帮助我所付出的一切努力。我将展示我能够生成的最终工作代码:

class Title
attr_accessor :string

def initialize(string)
@string = string
end

def fix
word_list = %w{a of and the}

a = string.downcase.split(' ')
b = []

a.each_with_index do |word, index|
  if index == 0 || !word_list.include?(word)
    b << word.capitalize
  else
    b << word
  end
end
b.join(' ')
end
end
课程名称
属性存取器:字符串
def初始化(字符串)
@字符串=字符串
结束
def修复
单词列表=%w{a of the}
a=string.downcase.split(“”)
b=[]
a、 每个带索引的单词,索引|
如果索引=0 | |!单词列表。包括?(单词)

这里有一个可能的解决办法

class Title
  attr_accessor :string

  IGNORES = %w( the of a and )

  def initialize(string)
    @string = string
  end

  def fix
    tokens = string.split(' ')
    tokens.map do |token|
      token = token.downcase

      if IGNORES.include?(token)
        token
      else
        token.capitalize
      end
    end.join(" ")
  end

end

Title.new("a title of a book").fix
你的出发点很好。以下是一些改进:

  • 比较总是小写的。这将简化if条件
  • 被忽略项的列表被放入一个数组中。这将简化if条件,因为您不需要为每个被忽略的字符串指定if(它们可能有数百个)
  • 我使用地图来替换代币。使用带有枚举的块循环项是一种常见的Ruby模式
满足所有条件:

  • a、 是、of、the,且全部小写
  • 将所有其他单词大写
  • 所有第一个单词都大写

  • 解释
  • string.downcase
    调用一个操作,使正在使用的字符串全部小写
  • .split(/(\s)/)
    采用小写字符串,并在空白处(空格、制表符、换行符等)将其拆分为一个数组,使每个单词成为数组的一个元素;括号中的
    \s
    (分隔符)周围也会将其保留在返回的数组中,因此在重新加入时不会丢失该空格字符
  • .map.使用_index{124; x,i
    迭代返回的数组,其中
    x
    是值,
    i
    是索引号;每次迭代返回一个新数组的元素;循环完成后,您将拥有一个新数组
  • (i==0 | | x.match(/^(?:a |是|和$/).nil?
    如果它是数组中的第一个元素(索引为0),或者单词匹配
    a
    ,或者
    ,也就是说,匹配不是
    ,那么
    x(它与忽略词匹配)因此只需返回单词/值,
    x
  • .join
    使用我们的新数组,再次将所有单词组合成一个字符串
  • 附加的
    • 通常,正则表达式中括号内的内容被视为捕获组,这意味着如果内部的模式匹配,则在正则表达式操作完成后,一个特殊变量将保留该值。在某些情况下,例如
      \s
      我们希望捕获该值,因为我们重用了该值,在其他情况下,例如忽略字we需要匹配,但不需要捕获它们。要避免捕获匹配,您可以在捕获组的开头调整步调
      ?:
      ,告诉正则表达式引擎不要保留该值。这有许多好处不属于此答案的范围

    • 有两种方法可以解决此问题:

      • 将字符串拆分为单词,可能修改每个单词并将单词重新连接在一起;或者
      • 使用正则表达式
      我会说一些关于后者的事情,但我相信你的练习涉及到前者——这是你所采取的方法——所以我将集中讨论这一点

      将字符串拆分为单词

      您可以使用
      String#split(“”)
      将字符串拆分为单词:

      str = "a title of a\t   book"
      a = str.split(' ')
        #=> ["a", "title", "of", "a", "book"] 
      
      这很好,即使有额外的空白,但通常会这样写:

      str.split
        #=> ["a", "title", "of", "a", "book"] 
      
      这两种方法都是相同的

      str.split(/\s+/)
        #=> ["a", "title", "of", "a", "book"] 
      
      请注意,我使用变量
      a
      表示数组是返回的。有些人可能觉得这不够描述性,但我认为它比
      s
      更好,这有点让人困惑。:-)

      创建枚举数

      接下来,发送方法
      Enumerable#each_with_index
      以创建枚举器:

      enum0 = a.each_with_index
        # => #<Enumerator: ["a", "title", "of", "a", "book"]:each_with_index> 
      
      您使用了带有索引的
      每个单词,因为第一个单词(索引为
      0的单词)的处理方式与其他单词不同。这很好

      到目前为止还不错,但此时您需要使用
      Enumerable#map
      enum0
      的每个元素转换为适当的值。例如,第一个值,
      [“a”,0]
      将转换为“a”,下一个值将转换为“Title”,第三个值将转换为“of”

      因此,您需要将方法
      Enumerable#map
      发送到
      enum0

      enum1 = enum.map
        #=> #<Enumerator: #<Enumerator: ["a", "title", "of", "a",
              "book"]:each_with_index>:map> 
      enum1.to_a
        #=> [["a", 0], ["title", 1], ["of", 2], ["a", 3], ["book", 4]] 
      
      最后,我们在每个单词之间加入一个空格:

      b.join(' ')
        => "A Title of a Book" 
      
      查看计算细节

      str = "a title of a book"
      
      str.gsub(/(^\w+)|(\w+)/) do
        $1 ? $1.capitalize :
          articles.include?($2) ? $2 : $2.capitalize
      end
        #=> "A Title of a Book" 
      
      让我们回到
      b
      的计算。
      enum1
      的第一个元素被传递到块中并分配给块变量:

      w, i = ["a", 0] #=> ["a", 0] 
      w               #=> "a" 
      i               #=> 0 
      
      因此,我们执行:

      case 0
      when 0 then "a".capitalize
      else articles.include?("a") ? "a".downcase : "a".capitalize
      end
      
      它返回
      “a”。大写=>“a”
      。类似地,当
      e的下一个元素
      
      w, i = ["a", 0] #=> ["a", 0] 
      w               #=> "a" 
      i               #=> 0 
      
      case 0
      when 0 then "a".capitalize
      else articles.include?("a") ? "a".downcase : "a".capitalize
      end
      
      w, i = ["title", 1] #=> ["title", 1] 
      w               #=> "title" 
      i               #=> 1 
      
      case 1
      when 0 then "title".capitalize
      else articles.include?("title") ? "title".downcase : "title".capitalize
      end
      
      w, i = ["of", 2] #=> ["of", 2] 
      w               #=> "of" 
      i               #=> 2 
      
      case 2
      when 0 then "of".capitalize
      else articles.include?("of") ? "of".downcase : "of".capitalize
      end
      
      str.split.each_with_index.map do |w,i|
        case i
        when 0 then w.capitalize
        else articles.include?(w) ? w.downcase : w.capitalize
        end
      end
        #=> ["A", "Title", "of", "a", "Book"] 
      
      first_word, *remaining_words = str.split
      first_word
        #=> "a" 
      remaining_words
        #=> ["title", "of", "a", "book"] 
      
      "#{first_word.capitalize} #{ remaining_words.map { |w|
        articles.include?(w) ? w.downcase : w.capitalize }.join(' ') }"
         #=> "A Title of a Book" 
      
      str = "a title of a book"
      
      str.gsub(/(^\w+)|(\w+)/) do
        $1 ? $1.capitalize :
          articles.include?($2) ? $2 : $2.capitalize
      end
        #=> "A Title of a Book" 
      
      class Title
        attr_accessor :str
        def initialize(str)
         @str = str
        end
      
        def fix
          s = str.downcase.split(" ") #convert all the strings to downcase and it will be stored in an array
          words_cap = []
          ignore = %w( of a and the ) # List of words to be ignored
          s.each do |item|
            if ignore.include?(item) # check whether word in an array is one of the words in ignore list.If it is yes, don't capitalize. 
              words_cap << item
      
            else
              words_cap << item.capitalize
            end  
          end
          sentence = words_cap.join(" ") # convert an array of strings to sentence
          new_sentence =sentence.slice(0,1).capitalize + sentence.slice(1..-1) #Capitalize first word of the sentence. Incase it is not capitalized while checking the ignore list.
        end
      
      
      end