Peta atau kurangi dengan indeks di Swift


143

Apakah ada cara untuk mendapatkan indeks array di mapatau reducedi Swift? Saya mencari sesuatu seperti each_with_indexdi 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")

Saya ingin menyingkirkan oddvariabel di atas .

Jawaban:


313

Anda dapat menggunakan enumerateuntuk mengonversi urutan ( Array,, Stringdll.) Ke urutan tupel dengan penghitung bilangan bulat dan elemen dipasangkan bersama. Itu adalah:

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

Tautan ke enumeratedefinisi

Perhatikan bahwa ini tidak sama dengan mendapatkan indeks koleksi — enumeratememberi Anda kembali penghitung bilangan bulat. Ini sama dengan indeks untuk array, tetapi pada string atau kamus tidak akan sangat berguna. Untuk mendapatkan indeks aktual bersama dengan setiap elemen, Anda dapat menggunakan zip:

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

Saat menggunakan urutan enumerasi dengan reduce, Anda tidak akan dapat memisahkan indeks dan elemen dalam sebuah tuple, karena Anda sudah memiliki akumulasi / tuple saat ini dalam tanda tangan metode. Sebagai gantinya, Anda harus menggunakan .0dan .1pada parameter kedua untuk reducepenutupan Anda :

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

Swift 3.0 dan lebih tinggi

Karena sintaks Swift 3.0 sangat berbeda.
Anda juga dapat menggunakan sintaks pendek / inline untuk memetakan array pada kamus:

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

Itu menghasilkan:

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

1
Tidak ingin mencuri kesenangan penyelesaian, jadi jika Anda membutuhkannya, saya letakkan cek Luhn yang dimodifikasi di intisari daripada di jawabannya: gist.github.com/natecook1000/1eb756d6b10297006137
Nate Cook

5
Dalam cepat 2.0 yang perlu Anda lakukan:numbers.enumerate().map { (index, element) in ...
Robert

@CharlieMartin: Anda dapat menggunakan .reducesetelah enumerate()atau zip.
Nate Cook

Tetapi dengan indeks? Saya mendapatkan kesalahan bahwa fungsi hanya membutuhkan dua parameter dan mengurangi mengambil objek inital (hasil dari pengurangan) sebagai param pertama dan nilai saat ini diulang sebagai yang kedua. Saya saat ini hanya menggunakan for..in sebagai gantinya
Charlie Martin

In Swift 5 enumeratesekarangenumerated
lustig

10

Karena Swift 2.1saya menulis fungsi selanjutnya:

extension Array {

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

Dan kemudian gunakan seperti ini:

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

8

Dengan Swift 3, ketika Anda memiliki objek yang sesuai dengan Sequenceprotokol dan Anda ingin menghubungkan setiap elemen di dalamnya dengan indeksnya, Anda bisa menggunakan enumerated()metode.

Sebagai contoh:

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

Oleh karena itu, dalam kasus paling sederhana, Anda dapat menerapkan algoritma Luhn di Playground seperti ini:

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

Jika Anda memulai dari String, Anda dapat menerapkannya seperti ini:

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

Jika Anda perlu mengulangi operasi itu, Anda dapat mengubah kode menjadi ekstensi:

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

Atau, dengan cara yang ringkas:

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

2

Selain contoh Nate Cook map, Anda juga dapat menerapkan perilaku ini 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]

Perhatikan bahwa EnumerateSequencemelewati ke penutupan sebagai enumeratedtidak dapat didekomposisi dalam cara bersarang, sehingga anggota tuple harus diuraikan di dalam penutupan (yaitu. enumerated.index).


2

Ini adalah ekstensi CollectionType yang berfungsi untuk swift 2.1 menggunakan throw and 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)
    }

}

Saya tahu ini bukan yang Anda minta, tetapi selesaikan masalah Anda. Anda dapat mencoba metode 2.0 Luhn cepat ini tanpa memperpanjang apa pun:

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
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.