web-dev-qa-db-fra.com

Carte ou réduction avec index dans Swift

Est-il possible d'obtenir l'index du tableau dans map ou reduce dans Swift? Je cherche quelque chose comme each_with_index en Ruby.

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")

Je voudrais me débarrasser de la variable oddci-dessus .

115
Jonas Elfström

Vous pouvez utiliser enumerate pour convertir une séquence (Array, String, etc.) en une séquence de n-uplets avec un compteur entier et un élément appariés. C'est:

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"]

Lien vers enumerate definition

Notez que cela ne revient pas à obtenir l'index de la collection —enumerate vous restitue un compteur d'entiers. C'est la même chose que l'index d'un tableau, mais sur une chaîne ou un dictionnaire ne sera pas très utile. Pour obtenir l'index réel avec chaque élément, vous pouvez utiliser Zip:

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

Lorsque vous utilisez une séquence énumérée avec reduce, vous ne pourrez pas séparer l'index et l'élément d'un tuple, car vous avez déjà le tuple accumulé/actuel dans la signature de la méthode. Au lieu de cela, vous devrez utiliser .0 et .1 sur le second paramètre de votre reduce clôture:

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

Swift 3.0

Depuis Swift 3.0 est assez différente.
Vous pouvez également utiliser syntaxe courte/en ligne pour mapper un tableau sur un dictionnaire:

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

Cela produit:

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

Pour Swift 2.1 J'ai écrit la fonction suivante:

extension Array {

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

Et puis utilisez-le comme ceci:

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

Avec Swift 3, lorsque vous avez un objet conforme au protocole Sequence et que vous souhaitez lier chaque élément à l'intérieur de celui-ci avec son index, vous pouvez utiliser enumerated() méthode.

Par exemple:

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")]

Par conséquent, dans le cas le plus simple, vous pouvez implémenter un algorithme de Luhn dans un terrain de jeu comme ceci:

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

Si vous partez d'un String, vous pouvez l'implémenter comme ceci:

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

Si vous devez répéter ces opérations, vous pouvez refactoriser votre code dans une extension:

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

Ou, d'une manière très concise:

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
7
Imanou Petit

Ceci est une extension de travail CollectionType pour Swift 2.1 utilisant des jets et des renversements:

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)
    }

}

Je sais que ce n'est pas ce que vous demandiez, mais résout votre problème. Vous pouvez essayer cette Swift 2.0 Luhn sans rien étendre:

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
}
2
Peter Stajger

En plus de l'exemple de Nate Cook de map, vous pouvez également appliquer ce comportement à 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]

Notez que le EnumerateSequence passé dans la fermeture sous la forme enumerated ne peut pas être décomposé de manière imbriquée, les membres du tuple doivent donc être décomposés à l'intérieur de la fermeture (ie. enumerated.index).

2
Levi McCallum