Swiftのmap
またはreduce
で配列のインデックスを取得する方法はありますか? Rubyでeach_with_index
のようなものを探しています。
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
変数 上記 を取り除きたいです。
enumerate
を使用して、シーケンス(Array
、String
など)を整数カウンターと要素がペアになったタプルのシーケンスに変換できます。あれは:
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"]
これは、コレクションのindexを取得することと同じではないことに注意してください。enumerate
は、整数カウンターを返します。これは配列のインデックスと同じですが、文字列または辞書ではあまり役に立ちません。各要素とともに実際のインデックスを取得するには、Zip
を使用できます。
let actualIndexAndNum: [String] = Zip(numbers.indices, numbers).map { "\($0): \($1)" }
print(actualIndexAndNum)
// ["0: 7", "1: 8", "2: 9", "3: 10"]
reduce
で列挙されたシーケンスを使用する場合、メソッドシグネチャに累積/現在のタプルが既にあるため、タプル内のインデックスと要素を分離することはできません。代わりに、reduce
クロージャーの2番目のパラメーターで.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構文はまったく異なるため。
また、短い構文/インラインを使用して辞書に配列をマッピングできます。
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)]
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)")
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")]
したがって、最も単純な場合、次のようにプレイグラウンドで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
これは動作しますCollectionTypeスローとリスローを使用するSwift 2.1の拡張:
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
}
ネイトクックの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]
EnumerateSequence
としてクロージャーに渡されたenumerated
はネストされた方法で分解できないことに注意してください。したがって、Tupleのメンバーはクロージャー内で分解する必要があります(つまりenumerated.index
)。