【问题标题】:Map or reduce with index in Swift在 Swift 中使用索引映射或减少
【发布时间】:2015-03-16 17:24:13
【问题描述】:

有没有办法在 Swift 中获取 mapreduce 中的数组索引?我在 Ruby 中寻找类似 @​​987654324@ 的东西。

func lunhCheck(number : String) -> Bool
{
    var odd = true;
    return reverse(number).map { String($0).toInt()! }.reduce(0) {
        odd = !odd
        return $0 + (odd ? ($1 == 9 ? 9 : ($1 * 2) % 9) : $1)
    }  % 10 == 0
}

lunhCheck("49927398716")
lunhCheck("49927398717")

我想摆脱odd 变量above

【问题讨论】:

    标签: swift functional-programming


    【解决方案1】:

    您可以使用enumerate 将序列(ArrayString 等)转换为具有整数计数器和元素配对的元组序列。那就是:

    let numbers = [7, 8, 9, 10]
    let indexAndNum: [String] = numbers.enumerate().map { (index, element) in
        return "\(index): \(element)"
    }
    print(indexAndNum)
    // ["0: 7", "1: 8", "2: 9", "3: 10"]
    

    Link to enumerate definition

    请注意,这与获取集合的索引 不同——enumerate 会返回一个整数计数器。这与数组的索引相同,但在字符串或字典上不会很有用。要获取每个元素的实际索引,您可以使用zip

    let actualIndexAndNum: [String] = zip(numbers.indices, numbers).map { "\($0): \($1)" }
    print(actualIndexAndNum)
    // ["0: 7", "1: 8", "2: 9", "3: 10"]
    

    当使用带有reduce 的枚举序列时,您将无法将元组中的索引和元素分开,因为您已经在方法签名中拥有累积/当前元组。相反,您需要在 reduce 闭包的第二个参数上使用 .0.1

    let summedProducts = numbers.enumerate().reduce(0) { (accumulate, current) in
        return accumulate + current.0 * current.1
        //                          ^           ^
        //                        index      element
    }
    print(summedProducts)   // 56
    

    Swift 3.0 及以上版本

    由于 Swift 3.0 的语法完全不同。
    此外,您可以使用短语法/内联将数组映射到字典:

    let numbers = [7, 8, 9, 10]
    let array: [(Int, Int)] = numbers.enumerated().map { ($0, $1) }
    //                                                     ^   ^
    //                                                   index element
    

    产生:

    [(0, 7), (1, 8), (2, 9), (3, 10)]
    

    【讨论】:

    • 不想偷走整理的乐趣,所以如果你需要的话,我把修改后的 Luhn 检查放在一个要点而不是答案中:gist.github.com/natecook1000/1eb756d6b10297006137
    • 在 swift 2.0 中你需要做:numbers.enumerate().map { (index, element) in ...
    • @CharlieMartin:您可以在enumerate()zip 之后使用.reduce
    • 但是有索引?我得到一个错误,该函数只需要两个参数,reduce 将初始对象(reduce 的结果)作为第一个参数,将迭代的当前值作为第二个参数。我目前只是使用 for..in 代替
    • 在 Swift 5 中 enumerate 现在是 enumerated
    【解决方案2】:

    对于Swift 2.1我写了下一个函数:

    extension Array {
    
     public func mapWithIndex<T> (f: (Int, Element) -> T) -> [T] {     
         return zip((self.startIndex ..< self.endIndex), self).map(f)
       }
     }
    

    然后像这样使用它:

        let numbers = [7, 8, 9, 10]
        let numbersWithIndex: [String] = numbers.mapWithIndex { (index, number) -> String in
            return "\(index): \(number)" 
        }
        print("Numbers: \(numbersWithIndex)")
    

    【讨论】:

      【解决方案3】:

      在 Swift 3 中,当您有一个符合 Sequence 协议的对象并且您希望将其中的每个元素与其索引链接起来时,您可以使用 enumerated() 方法。

      例如:

      let array = [1, 18, 32, 7]
      let enumerateSequence = array.enumerated() // type: EnumerateSequence<[Int]>
      let newArray = Array(enumerateSequence)
      print(newArray) // prints: [(0, 1), (1, 18), (2, 32), (3, 7)]
      
      let reverseRandomAccessCollection = [1, 18, 32, 7].reversed()
      let enumerateSequence = reverseRandomAccessCollection.enumerated() // type: EnumerateSequence<ReverseRandomAccessCollection<[Int]>>
      let newArray = Array(enumerateSequence)
      print(newArray) // prints: [(0, 7), (1, 32), (2, 18), (3, 1)]
      
      let reverseCollection = "8763".characters.reversed()
      let enumerateSequence = reverseCollection.enumerated() // type: EnumerateSequence<ReverseCollection<String.CharacterView>>
      let newArray = enumerateSequence.map { ($0.0 + 1, String($0.1) + "A") }
      print(newArray) // prints: [(1, "3A"), (2, "6A"), (3, "7A"), (4, "8A")]
      

      因此,在最简单的情况下,您可以像这样在 Playground 中实现 Luhn 算法:

      let array = [8, 7, 6, 3]
      let reversedArray = array.reversed()
      let enumerateSequence = reversedArray.enumerated()
      
      let luhnClosure = { (sum: Int, tuple: (index: Int, value: Int)) -> Int in
          let indexIsOdd = tuple.index % 2 == 1
          guard indexIsOdd else { return sum + tuple.value }
          let newValue = tuple.value == 9 ? 9 : tuple.value * 2 % 9
          return sum + newValue
      }
      
      let sum = enumerateSequence.reduce(0, luhnClosure)
      let bool = sum % 10 == 0
      print(bool) // prints: true
      

      如果你从String开始,你可以这样实现:

      let characterView = "8763".characters
      let mappedArray = characterView.flatMap { Int(String($0)) }
      let reversedArray = mappedArray.reversed()
      let enumerateSequence = reversedArray.enumerated()
      
      let luhnClosure = { (sum: Int, tuple: (index: Int, value: Int)) -> Int in
          let indexIsOdd = tuple.index % 2 == 1
          guard indexIsOdd else { return sum + tuple.value }
          let newValue = tuple.value == 9 ? 9 : tuple.value * 2 % 9
          return sum + newValue
      }
      
      let sum = enumerateSequence.reduce(0, luhnClosure)
      let bool = sum % 10 == 0
      print(bool) // prints: true
      

      如果需要重复这些操作,可以将代码重构为扩展:

      extension String {
      
          func luhnCheck() -> Bool {
              let characterView = self.characters
              let mappedArray = characterView.flatMap { Int(String($0)) }
              let reversedArray = mappedArray.reversed()
              let enumerateSequence = reversedArray.enumerated()
      
              let luhnClosure = { (sum: Int, tuple: (index: Int, value: Int)) -> Int in
                  let indexIsOdd = tuple.index % 2 == 1
                  guard indexIsOdd else { return sum + tuple.value }
                  let newValue = tuple.value == 9 ? 9 : tuple.value * 2 % 9
                  return sum + newValue
              }
      
              let sum = enumerateSequence.reduce(0, luhnClosure)
              return sum % 10 == 0
          }
      
      }
      
      let string = "8763"
      let luhnBool = string.luhnCheck()
      print(luhnBool) // prints: true
      

      或者,用更简洁的方式:

      extension String {
      
          func luhnCheck() -> Bool {
              let sum = characters
                  .flatMap { Int(String($0)) }
                  .reversed()
                  .enumerated()
                  .reduce(0) {
                      let indexIsOdd = $1.0 % 2 == 1
                      guard indexIsOdd else { return $0 + $1.1 }
                      return $0 + ($1.1 == 9 ? 9 : $1.1 * 2 % 9)
              }
              return sum % 10 == 0
          }
      
      }
      
      let string = "8763"
      let luhnBool = string.luhnCheck()
      print(luhnBool) // prints: true
      

      【讨论】:

        【解决方案4】:

        除了 Nate Cook 的 map 示例之外,您还可以将此行为应用于 reduce

        let numbers = [1,2,3,4,5]
        let indexedNumbers = reduce(numbers, [:]) { (memo, enumerated) -> [Int: Int] in
            return memo[enumerated.index] = enumerated.element
        }
        // [0: 1, 1: 2, 2: 3, 3: 4, 4: 5]
        

        请注意,作为enumerated 传入闭包的EnumerateSequence 不能以嵌套方式分解,因此元组的成员必须在闭包内分解(即enumerated.index)。

        【讨论】:

          【解决方案5】:

          这是一个适用于 swift 2.1 的 CollectionType 扩展,使用 throws 和 rethrows:

          extension CollectionType {
          
              func map<T>(@noescape transform: (Self.Index, Self.Generator.Element) throws -> T) rethrows -> [T] {
                  return try zip((self.startIndex ..< self.endIndex), self).map(transform)
              }
          
          }
          

          我知道这不是您要问的,但可以解决您的问题。你可以试试这个 swift 2.0 Luhn 方法,无需扩展任何东西:

          func luhn(string: String) -> Bool {
              var sum = 0
              for (idx, value) in string.characters.reverse().map( { Int(String($0))! }).enumerate() {
                  sum += ((idx % 2 == 1) ? (value == 9 ? 9 : (value * 2) % 9) : value)
              }
              return sum > 0 ? sum % 10 == 0 : false
          }
          

          【讨论】:

            猜你喜欢
            • 2018-08-14
            • 1970-01-01
            • 2013-07-15
            • 1970-01-01
            • 1970-01-01
            • 2015-06-14
            • 2016-01-22
            • 1970-01-01
            • 2012-06-04
            相关资源
            最近更新 更多