【问题标题】:Modifying an array item in Ruby if it includes a specific word如果包含特定单词,则在 Ruby 中修改数组项
【发布时间】:2026-02-15 05:00:01
【问题描述】:

我找不到这个问题的解决方案,我进行了研究以找到问题并解决它们,但还想不出任何答案。

我要做的是将字符串转换为标题大小写的字符串

例如: 《指环王》>《指环王》

(如您所见,第一个单词总是大写的,如果是文章则无所谓,但如果字符串中有文章单词,则应该小写,如上例,并大写任何其他不是的单词)。

这是我要解决的练习的规范 (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

这是我的尝试,到目前为止我所做的:

class Title
  def initialize(string)
    @string = string
  end

  def fix
    @string.split.each_with_index do |element, index|
      if index == 0
        p element.capitalize!
      elsif index == 1
        if element.include?('Is') || element.include?('is')
          p element.downcase!
        end
      end
    end
  end
end

a = Title.new("this Is The End").fix
p a

输出:

“这个”

“是”

=> ["This", "is", "The", "End"]


我想做什么:

  1. 创建一个名为 Title 的类并用一个字符串对其进行初始化。
  2. 创建一个名为 fix 的方法,到目前为止,只检查索引 0 @string.split.each_with_index 方法(循环 通过),并打印element.capitalize!(注意“砰”,即 应该修改原始字符串,正如您在输出中看到的那样 以上)
  3. 我的代码所做的是检查索引 1(第二个字)和 调用.include?('is')看第二个词是不是文章, 如果是(使用 if 语句),则调用 element.downcase!, 如果没有,我可以为索引创建更多检查(但我意识到 这里是一些字符串可以由 3 个单词组成,另一些由 5 个单词组成, 其他人 10,依此类推,所以我的代码对此效率不高, 这是我无法解决的问题。

也许创建一个文章单词列表并使用 .include 进行检查?如果列表中有一些单词,方法是什么? (我试过这个,但 .include? 方法只接受字符串而不是数组变量,我试过 join(' ') 方法但没有运气)。

非常感谢! 真的

【问题讨论】:

  • 您可以通过使用case 语句或Set 来匹配您想要小写的单词,从而大大消除这个问题。
  • 我运行了你的代码,它返回了["This", "is", "The", "End"],这与你发布的不符。
  • 感谢@Jordan,它现在是最新的。
  • 因为问题涉及改进(到目前为止)工作正常的代码,它可能更适合Code Review Stack Exchange
  • 你只关心索引 0 或索引不 0,不关心索引 1。

标签: ruby bloc.io


【解决方案1】:

我喜欢将这些类型的问题分解成更小的逻辑块,以帮助我在编写算法之前理解。在这种情况下,您需要根据一些规则修改字符串的每个单词。

  1. 如果是第一个单词,请将其大写。
  2. 如果不是特殊词,请大写。
  3. 如果是特殊词且不是第一个词,请将其小写。

通过这些规则,您可以编写要遵循的逻辑。

special_words = ['a', 'an', 'and', 'of', 'the']
fixed_words = []
@string.downcase.split.each_with_index do |word, index|
    # If this isn't the first word, and it's special, use downcase
    if index > 0 and special_words.include?(word)
      fixed_words << word
    # It's either the first word, or not special, so capitalize
    else
      fixed_words << word.capitalize
    end
end
fixed_words.join(" ")

您会注意到我在调用 split 和 each_with_index 之前对字符串使用了小写。这样一来,所有单词都会被规范化为小写,并且可以轻松地对照 special_words 数组进行检查。

我还将这些转换后的单词存储在一个数组中,最后将它们重新组合在一起。原因是,如果我尝试使用小写字母!或大写!在拆分字符串上,我没有修改原始标题字符串。

注意:这个问题是 Bloc Full Stack 课程作业的一部分,这就是为什么我使用简化的解决方案,而不是一个衬垫、模块、文件 io 等。

【讨论】:

    【解决方案2】:

    我会在与您的程序相同的文件夹中创建一个新文件(在我的示例中,该文件称为“exclude.txt”,并将“and, the”之类的词全部放在新行上。然后我会做像这样:

    class String
      def capitalize_first_letter
        self[0].upcase + self[1..-1]
      end
    end
    
    class Title
      def initialize(string)
        @string = string
        @exclude_words = File.readlines('exclude.txt').map(&:downcase).map(&:chomp)
      end
    
      def fix
        @string.downcase.gsub(/\S+/) do |word|
          @exclude_words.include?(word) ? word : word.capitalize
        end.capitalize_first_letter
      end
    end
    

    假设您的 exclude.txt 文件包含您希望保持小写的单词(我的包含 of、on、a、is、the 和),那么您的所有测试都应该通过:

    p Title.new("the great gatsby").fix #=> "The Great Gatsby"
    

    【讨论】:

      【解决方案3】:

      我相信这里的模块比类更合适,因为不需要“标题实例”——你以字符串开头,以字符串结尾。如果标题有更多方法,那么可能需要一个类。这是我的实现:

      您丢失的部分是Array#map。一般来说,从基础 ruby​​ 类(数组、字符串、哈希等)中学习尽可能多的方法始终是一项不错的投资。

      module Title
        DEFAULT_NON_CAPITALIZED_WORDS = %w{a an the and but for on at to or}
      
        def self.titleize(str, nocaps = DEFAULT_NON_CAPITALIZED_WORDS)
          str = str.downcase.split.map{|w| nocaps.include?(w) ? w : w.capitalize}.join(' ')
          str[0].capitalize + str[1..-1]
        end
      end
      

      测试:

      puts Title.titleize("the great gatsby")
      puts Title.titleize("liTTle reD Riding hOOD")
      puts Title.titleize("The sword And The stone")
      puts Title.titleize("THE SWORD AND THE STONE")
      

      编辑:它可以在一个长行中完成,但它需要一个正则表达式来进行初始大写:

      str.downcase.split.map{|w| nocaps.include?(w) ? w : w.capitalize}.join(' ').sub(/^./, &:upcase)
      

      【讨论】:

        【解决方案4】:

        由于您希望输出为字符串,因此模块方法可能更合适。下面这个模块,方便扩展,比较清晰。

        看起来像这样:

        module Title
          SMALL_WORDS = %w(a an the at by for in of on to up and as but it or nor)
        
          def self.titleize(str)
            # Return the original string if it is empty
            return str if str.match(/\A\w*\Z)
        
            # Split the name into an array of words
            words = name.split(' ')
        
            # Capitalize every word that is not a small word
            words.each do |word|
              word[0] = word[0].upcase unless SMALL_WORDS.include? word.downcase
            end
        
            # Capitalize the first and last words
            words.each_with_index do |word, index|
              if index == 0 || index == words.count - 1
                word[0] = word[0].upcase
              end
            end
        
            # Return the words as a string
            words.join(' ')
          end
        end
        

        它的工作原理是这样的:

        Title.titleize('the USA swimming association')
          # => 'The USA Swimming Association'
        Title.titleize('the great charter of the liberties of england')
          # => 'The Great Charter of the Liberties of England'
        Title.titleize('  a      history  of of ')
          # => 'A History of Of'
        

        在创建一个不错的 titleize 函数时需要考虑几个边缘情况:

        • 最后一个单词无论如何都应该大写(即使是像“of”这样的单词)
        • 应保留首字母缩略词(如“USA”)

        【讨论】:

          【解决方案5】:

          我们需要使用 each_with_index 和 if 语句。请注意我如何确保他们忽略使用 index > 0 制作的第一篇文章“the”。祝你好运!

              class Title
            attr_accessor :string
            Articles = %w( an the of a and )
          
            def initialize(string)
              @string = string
            end
          
            def fix
              fixed_words = []
              @string.downcase.split.each_with_index do |word, index|
                if index > 0 && Articles.include?(word)
                  fixed_words << word
                else
                  fixed_words << word.capitalize
                end
              end
              fixed_words.join(" ")
            end
          end
          
          p Title.new("the great gatsby").fix
          p Title.new("liTTle reD Riding hOOD").fix
          p Title.new("The lord of the rings").fix
          p Title.new("The sword And The stone").fix
          p Title.new("the portrait of a lady").fix
          p Title.new("THE SWORD AND THE STONE").fix
          

          【讨论】:

            最近更新 更多