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 odd
ci-dessus .
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
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)]
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)")
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
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
}
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
).