【问题标题】:What is the most efficient way to get the first character of each word in a string in Swift?在 Swift 中获取字符串中每个单词的第一个字符的最有效方法是什么?
【发布时间】:2019-03-12 14:37:06
【问题描述】:

获取字符串中每个单词的第一个字符的最简洁的方法是什么?

开始:

let str = "First Middle Last"

预期输出:

FML

您会使用 for 循环来获取每个单词的前缀、map 函数还是 reduce - 最有效的方法是什么?

【问题讨论】:

  • 简洁和高效是两个不同的东西——你在找什么?
  • 请注意,有一个专门的类 PersonNameComponentsFormatter 用于此目的,比较stackoverflow.com/a/51371892

标签: swift string


【解决方案1】:

你可以这样实现:

let result = str.components(separatedBy: " ").map { String($0.prefix(1))}.joined()

【讨论】:

    【解决方案2】:

    您可以使用components(separatedBy:) 将字符串拆分为每个空格字符上的单词(或空格,如果您需要的话,包括换行符),然后在[String] 上调用map 以获取每个字符的第一个字符词。

    let startingLetters = str.components(separatedBy: " ").map{$0.prefix(1)} // ["F", "M", "L"]
    

    如果您想将这些字母组合成一个String,您应该使用reduce 而不是map(这里我还使用了包含所有空格和换行符的CharacterSet,而不是硬编码的@987654330 @ 用于演示目的,请选择最适合您的确切需求的分隔符。

    let joinedStartingLetters = str.components(separatedBy: CharacterSet.whitespacesAndNewlines).reduce("", {$0+$1.prefix(1)}) // "FML"
    

    【讨论】:

    • 嗨,大卫。希望这会以某种方式有用:stackoverflow.com/questions/48850292/…
    • @AhmadF 一般来说,我同意joined 可能是比reduce 更好的连接字符串的选择,但在这种特殊情况下,joined 只能在执行之后调用map,而reduce 可以通过对字符串的单次迭代来实现。
    【解决方案3】:

    我相信最有效的方法是遍历字符:

    let str = "First Middle Last"
    
    let beforeLast = str.isEmpty ? str.startIndex : str.index(before: str.endIndex)
    
    var result = ""
    
    var c = str.startIndex
    
    OuterLoop: do {
        while c < str.endIndex {
    
            while str[c] == " " {
                if c < beforeLast { c = str.index(after: c) } 
                else { break OuterLoop }
            }
    
            result.append(str[c])
    
            while str[c] != " " {
                if c < beforeLast { c = str.index(after: c) } 
                else { break OuterLoop }
            }
        }
    }
    
    print(result)   //"FML"
    

    这比创建单个单词的中间数组,然后为元素添加前缀并将前缀转换为 String,然后连接该新数组的元素更有效。

    Try It Online!(只考虑CPU占有率较高的结果,单独运行代码)

    以下是一些基准:

    @RamyMohamed's solution      : 0.000708s
    @DávidPásztor's 2nd solution : 0.000669s //separatedBy: CharacterSet.whitespacesAndNewlines 
    @AhmadF's solution           : 0.000561s
    @DávidPásztor's 1st solution : 0.000550  //separatedBy: " "
    This solution                : 0.000110s
    

    如果您想考虑换行和制表,一个简单的解决方案是在 Character 上定义扩展:

    extension Character {
        static let blanks = [" ", "\n", "\t", "\r"]
        func isBlank() -> Bool {
            return Character.blanks.contains(String(self))
        }
    }
    

    并像这样使用它:

    OuterLoop: do {
        while c < str.endIndex {
            while str[c].isBlank() {
                if c < beforeLast { c = str.index(after: c) } 
                else { break OuterLoop }
            }
    
            result.append(str[c])
    
            while !str[c].isBlank() {
                if c < beforeLast { c = str.index(after: c) } 
                else { break OuterLoop }
            }
        }
    }
    

    时钟在0.000174s

    【讨论】:

      【解决方案4】:

      您可以使用正则表达式来获取它。

       "Fried Hello Night".replacingOccurrences(of: "\\B[^\\s]*(\\s)?", with: "", options: .regularExpression, range: nil)
      

      另一个是如果第一个字母是大写的,只需使用过滤器。

       String("Fried Hello Night".unicodeScalars.filter{CharacterSet.uppercaseLetters.contains($0)})
      

      【讨论】:

        【解决方案5】:

        您希望对每个单词都执行此操作,这让您别无选择,只能遍历整个字符序列。

        不过,您可以通过扩展 String 对象在 Swift 中优雅地做到这一点

        extension String
        {
           public func getAcronym(separator: String = "") -> String
          {
            let acronym = self.components(separatedBy: CharacterSet.whitespacesAndNewlines).map({ 
              String($0.characters.first!) }).joined(separator: separator)
            return acronym
           }
        }
        

        然后像这样使用它:

        let acronym = "First Middle Last".getAcronym()
        //output: FML
        
        let acronymDotted = "First Middle Last".getAcronym(separator: ".")
        //output: F.M.L
        

        注意:我添加了CharacterSet.whitespacesAndNewlines 以忽略单词之间的多余空格,因此它会自动清除字符串。

        【讨论】:

        • 不要在 Swift 中使用旧的 Foundation 类型,因为它们具有 Swift 等效项(CharacterSet 而不是 NSCharacterSet 并且不要将 ; 放在行尾,这是不是 Objective-C。
        • @DávidPásztor 谢谢你的通知,我更新了我的答案
        猜你喜欢
        • 2011-06-10
        • 1970-01-01
        • 1970-01-01
        • 2022-08-08
        • 1970-01-01
        • 2015-04-11
        • 2022-12-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多