【问题标题】:Swift replace string in array using for-in loop使用 for-in 循环快速替换数组中的字符串
【发布时间】:2016-03-04 17:41:49
【问题描述】:

作为学习练习,如果StringDictionary 中的现有密钥。我已经知道如何使用.stringByReplacingOccurrencesOfString 执行此操作,但我想了解我在这里做错了什么,以及如何使用 for-in 循环来完成此操作。

let sillyMonkeyString = "A monkey stole my iPhone"
let dictionary = ["monkey": "????", "iPhone":"????"]
var toArray = sillyMonkeyString.componentsSeparatedByString(" ")
// desired result is "A ???? stole my ????"

以下是行不通的:

for var str in toArray {
    if let val = dictionary[str] {
        str = val                  
    }
}
let backToString = toArray.joinWithSeparator(" ")

有什么作用:

var newArray = [String]()
for var str in toArray {
    if let val = dictionary[str] {
        str = val
    }
    newArray.append(str)
}
let backToString = newArray.joinWithSeparator(" ")

这很有效,因为我正在创建一个新数组并附加到新数组。但是,我原来的 Array 可变的,那么为什么第一个解决方案没有在 for-in 循环内的原始 Array 中正确地将 str 分配给 val 呢?我发现的唯一其他相关问题here 有一个很好的单行答案,但没有解释我是否可以使用 for-in 循环来完成此操作。

更新:我不建议为这个特定用例实现 for-in 循环。我问这个问题是为了了解如何做到这一点。如果用户想用字典替换部分字符串,我强烈建议考虑使用以下更高效、更快捷的解决方案之一(这可能不是公认的解决方案)

【问题讨论】:

  • 既然您是在尝试自学 - 这里有一个额外的挑战:在数组上使用 reduce 函数,而不是使用 for-in
  • 我也上过 udacity 课程
  • 您可以在循环中使用 for(请参阅我的回答),但我建议您使用更“实用”且更快捷的解决方案

标签: arrays string swift for-in-loop


【解决方案1】:

“纯”Swift(无导入 Foundation)和“功能”方式导致

let sillyMonkeyString = "A monkey stole my iPhone"
let dictionary = ["monkey": "?", "iPhone":"?"]

let arr = sillyMonkeyString.characters.split(" ").map(String.init)
let result = arr.map { dictionary[$0] ?? $0 }.joinWithSeparator(" ")

print(result)
// A ? stole my ?

如果需要for in loop,则比

var resArr:[String] = []
for txt in arr {
    resArr.append(dictionary[txt] ?? txt)
}
let result2 = resArr.joinWithSeparator(" ") // A ? stole my ?"

如果您想要变异解决方案...

let sillyMonkeyString = "A monkey stole my iPhone"
let dictionary = ["monkey": "?", "iPhone":"?"]

var arr = sillyMonkeyString.characters.split(" ").map(String.init)

for (idx,value) in arr.enumerate() {
    arr[idx] = dictionary[value] ?? value
}
let result3 = arr.joinWithSeparator(" ") // "A ? stole my ?"

如果你不喜欢 enumerate(),你可以使用索引来代替

let sillyMonkeyString = "A monkey stole my iPhone"
let dictionary = ["monkey": "?", "iPhone":"?"]

var arr = sillyMonkeyString.characters.split(" ").map(String.init)

for i in arr.indices {
    arr[i] = dictionary[arr[i]] ?? arr[i]
}
let result4 = arr.joinWithSeparator(" ") // "A ? stole my ?"

【讨论】:

  • 你的第二个答案是我最初在我的问题中发布的,因为我知道如果我创建一个新数组它会起作用,但想知道如何避免这样做。
  • @jungledev 好的,我追加了“变异”解决方案:-)
【解决方案2】:

在 for 循环或“if var”(已弃用)语句中将某些内容声明为“var”时,值将复制到该变量,而不是引用。因此,当您更改“val”时,您正在更改该局部变量,而不是更改 toArray 中存在的对象

如果您想在数组中更改它,这应该可以工作(未编译仅供参考):

for str in toArray {
    if let val = dictionary[str] {
        toArray[toArray.indexOf(str)] = val
    }
}

或者,您可以改为跟踪索引

for i in 0 ..< toArray.count {
    if let val = dictionary[toArray[i]] {
        toArray[i] = val
    }
}

要为您发送的链接提供一个可比较的示例,您可以执行以下操作:

toArray = toArray.map { dictionary[$0] ?? $0 }

如果您不熟悉地图功能,我建议您查找它,因为它是 swift 中最好的功能之一! :)

【讨论】:

  • 我了解映射。我很好奇为什么我正在处理的练习挑战明确要求我使用 for-in 循环来解决它。我的解决方案感觉很老套,但如果挑战是要求换入,我假设有一个优雅的解决方案有待发现。我还不能让你的第一个解决方案起作用。我觉得除了使用索引之外应该有另一种方法。继续……(感谢您的帮助=))
  • 发现了一个潜在的错误。我认为应该是 .indexOf(str) (答案已更新)
  • 非常接近。我刚刚想通了。您的解决方案引发了一个可选错误。我已经添加了我的。
  • 虽然它们都是有效的解决方案,但很高兴注意到第二个解决方案的性能更高(您可能会在长字符串上注意到这一点) - 每次调用 toArray.indexOf(str) 时,函数都必须遍历数组以查找索引,如第二个解决方案所示,这是不必要的。
  • 是的,如果你想做纯粹的快速枚举,它会受到限制。您的另一个选择是跟踪独立于循环的索引。
【解决方案3】:

str = val 只会更改局部变量中的值,该值仅在 for 循环范围内有效。数组中的值不会改变。你真正想要的是:

for i in 0..< toArray.count {
    if let val = dictionary[toArray[i]] {
        toArray[i] = val
    }
}

【讨论】:

  • 仅供参考,这种传统的 C 风格 for 循环在 Swift 2.2 中已弃用,将在 Swift 3.0 中删除
  • @PEEJWEEJ 谢谢。更新为新的 swift 语法。
  • 您开始在索引 1 处循环,这将导致 indexOutOfBounds 错误。它应该是“for i in 0..
  • 仍然会因越界而崩溃( ... 应该是 ..
【解决方案4】:

您可以使用 forEach 遍历您的字典键/值对并将它们替换如下:

var sillyMonkeyString = "A monkey stole my iPhone"
let dictionary = ["monkey": "?", "iPhone":"?"]

dictionary.forEach{ sillyMonkeyString = sillyMonkeyString.stringByReplacingOccurrencesOfString($0.0, withString: $0.1) }
print(sillyMonkeyString)  // "A ? stole my ?"

【讨论】:

    【解决方案5】:

    这个方法怎么样:

    var sillyMonkeyString = "A monkey stole my iPhone"
    let dictionary = ["monkey": "?", "iPhone":"?"]
    var toArray = sillyMonkeyString.componentsSeparatedByString(" ")
    
    for (index, string) in toArray.enumerate()
    {
      if let val = dictionary[string]
      {
        toArray[index] = val
      }
    }
    let finalString = toArray.joinWithSeparator(" ")
    

    该代码使用 for...in 的变体,它将数组中的对象和索引作为元组返回。其他一些答案使用了indexOfindexOf 在计算上既昂贵又具有潜在风险。数组可以包含同一对象的多个副本,因此相关字符串可能出现在多个位置。在这个特定的示例中,它之所以有效,是因为我们从前到后循环遍历单词数组,同时替换匹配的单词。因此, indexOf 将始终找到字符串的第一个剩余匹配项。尽管如此,它在大型阵列上仍然很慢。

    【讨论】:

      【解决方案6】:

      这是我想出的解决方案。我会考虑使用其他人,因为许多人建议使用 .indexOf 非常昂贵。我没有分配给 ivar,而是将 val 直接分配给原始数组中当前索引处的项目。我完全赞成学习和实施成本最低的东西。但是,您必须承认这是干净而简短的。 (虽然不是最有效的)

      for var str in toArray {
          if let val = dictionary[str] {
              toArray[toArray.indexOf(str)!] = val
          }
      }
      

      【讨论】:

      • 只是一个提示:indexOf 可能很昂贵(它每次都从头开始枚举)并且如果数组中有相等的对象(它返回相等对象的第一个索引),则可能会失败。
      • 我通过使用枚举方法或维护我自己的索引变量来避免使用它。
      • 请详细说明这段代码是如何回答这个问题的。
      【解决方案7】:

      关于不起作用的代码中的“出了什么问题”的话题:

       for var str in toArray {
           if let val = dictionary[str] {
               str = val                  
           }
       }
       let backToString = toArray.joinWithSeparator(" ")
      

      for 循环中的 str 变量不是对 toArray 元素的引用,而是它的副本。所以在循环内修改 str 对它来自的数组没有影响。

      【讨论】:

        【解决方案8】:

        正如其他人所说,而不是

        for var str in toArray {
        

        如果必须是

        for str in toArray {
        

        但我想展示另一种方法,在字典上使用 enumerate()

        var sillyMonkeyString = "A monkey stole my iPhone"
        let dictionary = ["monkey": "?", "iPhone":"?"]
        
        for (_ ,v) in dictionary.enumerate() {
            sillyMonkeyString = sillyMonkeyString.stringByReplacingOccurrencesOfString(v.0, withString: v.1)
        }
        
        print(sillyMonkeyString)
        

        打印A ? stole my ?

        enumerate() 返回一个包含索引的元组以及带有键和值的元组。

        为了让坦佐隆开心,虽然这是一种相当愚蠢的方法:

        let sillyMonkeyString = "A monkey stole my iPhone"
        let dictionary = ["monkey": "?", "iPhone":"?"]
        var toArray = sillyMonkeyString.componentsSeparatedByString(" ")
        
        for (i,v) in dictionary.enumerate() {
            for (j,s) in toArray.enumerate() {
                if s == v.0 {
                    toArray[j] = v.1
                }
            }
        }
        
        print(toArray.joinWithSeparator(" "))
        

        for in 枚举

        var sillyMonkeyString = "A monkey stole my iPhone"
        let dictionary = ["monkey": "?", "iPhone":"?"]
        
        for (k,v) in dictionary{
            sillyMonkeyString = sillyMonkeyString.stringByReplacingOccurrencesOfString(k, withString: v)
        }
        
        print(sillyMonkeyString)
        

        【讨论】:

        • PO 明确表示:“我已经知道如何使用 .stringByReplacingOccurrencesOfString 来做到这一点,但我想了解我在这里做错了什么,以及如何使用 for-in 来完成这个循环。”
        • 这里的主要重点是我使用了 OP 要求的正确枚举(for in)。您的回答违反了问题的主要方面。
        • 那么,.enumerate 真的更便宜,因为它将数组转换为字典并且只枚举一次?真的很享受这段对话 =)
        • 在 BigO 中你可以说 indexOf 使任何 n^x 到 n^(x+1) 的算法,因为它每次调用都有 n 次并且执行 n 次。 enumerate 不会转换任何内容。根据你的称呼,它会返回一个索引和一些对象的元组。当我枚举字典时,元组由索引和表示字典键和值的元组组成。 enumerate 更便宜,因为它是线性的,不需要额外查找索引,并且保证是正确的。
        • @vikingosegundo for (_ ,v) in dictionary.enumerate() 如果要丢弃索引,为什么要使用枚举?
        猜你喜欢
        • 1970-01-01
        • 2017-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-02
        • 2015-12-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多