Bagaimana cara mengulang loop dengan indeks dan elemen di Swift

784

Apakah ada fungsi yang bisa saya gunakan untuk beralih pada array dan memiliki indeks dan elemen, seperti penghitungan Python?

for index, element in enumerate(list):
    ...
pemikir3
sumber

Jawaban:

1662

Iya. Pada Swift 3.0, jika Anda memerlukan indeks untuk setiap elemen beserta nilainya, Anda dapat menggunakan enumerated()metode ini untuk beralih ke array. Ini mengembalikan urutan pasangan yang terdiri dari indeks dan nilai untuk setiap item dalam array. Sebagai contoh:

for (index, element) in list.enumerated() {
  print("Item \(index): \(element)")
}

Sebelum Swift 3.0 dan setelah Swift 2.0, fungsinya disebut enumerate():

for (index, element) in list.enumerate() {
    print("Item \(index): \(element)")
}

Sebelum Swift 2.0, enumerateadalah fungsi global.

for (index, element) in enumerate(list) {
    println("Item \(index): \(element)")
}
Cezar
sumber
3
Meskipun tampak seperti sebuah tuple, di Swift 1.2 - tidak yakin tentang 2.0 - enumerate mengembalikan sebuah enumerateSequence <base: SequenceType> struct.
nstein
2
@Leviathlon kinerja overhead yang nyata atau dapat diukur yang penting? Tidak.
Dan Rosenstark
Apakah mendapatkan akses ke indeks adalah satu-satunya manfaat menggunakan enumerate?
Sayang
29
Mungkin mereka akan berubah menjadi gerund, enumeratinguntuk Swift 4. Menyenangkan!
Dan Rosenstark
3
@Sayang for (index, element) insaat menggunakan enumerateditu menyesatkan. seharusnyafor (offset, element) in
Leo Dabus
116

Swift 5 menyediakan sebuah metode yang disebut enumerated()untuk Array. enumerated()memiliki deklarasi berikut:

func enumerated() -> EnumeratedSequence<Array<Element>>

Mengembalikan urutan pasangan (n, x), di mana n merupakan bilangan bulat berturut-turut mulai dari nol dan x mewakili elemen urutan.


Dalam kasus paling sederhana, Anda dapat menggunakan enumerated()dengan for loop. Sebagai contoh:

let list = ["Car", "Bike", "Plane", "Boat"]
for (index, element) in list.enumerated() {
    print(index, ":", element)
}

/*
prints:
0 : Car
1 : Bike
2 : Plane
3 : Boat
*/

Perhatikan bahwa Anda tidak terbatas untuk menggunakan enumerated()dengan for loop. Bahkan, jika Anda berencana untuk menggunakan enumerated()dengan for loop untuk sesuatu yang mirip dengan kode berikut, Anda salah melakukannya:

let list = [Int](1...5)
var arrayOfTuples = [(Int, Int)]()

for (index, element) in list.enumerated() {
    arrayOfTuples += [(index, element)]
}

print(arrayOfTuples) // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]

Cara yang lebih cepat untuk melakukan ini adalah:

let list = [Int](1...5)
let arrayOfTuples = Array(list.enumerated())
print(arrayOfTuples) // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element: 3), (offset: 3, element: 4), (offset: 4, element: 5)]

Sebagai alternatif, Anda juga dapat menggunakan enumerated()dengan map:

let list = [Int](1...5)
let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] }
print(arrayOfDictionaries) // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]

Selain itu, meskipun memiliki beberapa keterbatasan , forEachbisa menjadi pengganti yang baik untuk for for:

let list = [Int](1...5)
list.reversed().enumerated().forEach { print($0, ":", $1) }

/*
prints:
0 : 5
1 : 4
2 : 3
3 : 2
4 : 1
*/

Dengan menggunakan enumerated()dan makeIterator(), Anda bahkan dapat mengulang secara manual di Array. Sebagai contoh:

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    var generator = ["Car", "Bike", "Plane", "Boat"].enumerated().makeIterator()

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(type: .system)
        button.setTitle("Tap", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        button.addTarget(self, action: #selector(iterate(_:)), for: .touchUpInside)
        view.addSubview(button)
    }

    @objc func iterate(_ sender: UIButton) {
        let tuple = generator.next()
        print(String(describing: tuple))
    }

}

PlaygroundPage.current.liveView = ViewController()

/*
 Optional((offset: 0, element: "Car"))
 Optional((offset: 1, element: "Bike"))
 Optional((offset: 2, element: "Plane"))
 Optional((offset: 3, element: "Boat"))
 nil
 nil
 nil
 */
Imanou Petit
sumber
1
Apakah mendapatkan akses ke indeks adalah satu-satunya manfaat menggunakan enumerate?
Sayang
52

Dimulai dengan Swift 2, fungsi enumerate perlu dipanggil pada koleksi seperti:

for (index, element) in list.enumerate() {
    print("Item \(index): \(element)")
}
Ricky
sumber
47

Saya menemukan jawaban ini sambil mencari cara untuk melakukannya dengan Kamus , dan ternyata cukup mudah untuk mengadaptasinya, cukup berikan tuple untuk elemen tersebut.

// Swift 2

var list = ["a": 1, "b": 2]

for (index, (letter, value)) in list.enumerate() {
    print("Item \(index): \(letter) \(value)")
}
Nycen
sumber
18

Anda cukup menggunakan loop enumerasi untuk mendapatkan hasil yang Anda inginkan:

Swift 2:

for (index, element) in elements.enumerate() {
    print("\(index): \(element)")
}

Swift 3 & 4:

for (index, element) in elements.enumerated() {
    print("\(index): \(element)")
}

Atau Anda bisa melalui loop for untuk mendapatkan hasil yang sama:

for index in 0..<elements.count {
    let element = elements[index]
    print("\(index): \(element)")
}

Semoga ini bisa membantu.

Riajur Rahman
sumber
14

Penghitungan dasar

for (index, element) in arrayOfValues.enumerate() {
// do something useful
}

atau dengan Swift 3 ...

for (index, element) in arrayOfValues.enumerated() {
// do something useful
}

Hitung, Saring, dan Peta

Namun, saya paling sering menggunakan enumerate dalam kombinasi dengan peta atau filter. Misalnya dengan mengoperasikan beberapa array.

Dalam array ini saya ingin memfilter elemen yang aneh atau bahkan diindeks dan mengubahnya dari Ints ke Doubles. Jadi enumerate()membuat Anda mengindeks dan elemen, lalu menyaring memeriksa indeks, dan akhirnya untuk menyingkirkan tuple yang dihasilkan saya memetakannya hanya elemen.

let evens = arrayOfValues.enumerate().filter({
                            (index: Int, element: Int) -> Bool in
                            return index % 2 == 0
                        }).map({ (_: Int, element: Int) -> Double in
                            return Double(element)
                        })
let odds = arrayOfValues.enumerate().filter({
                            (index: Int, element: Int) -> Bool in
                            return index % 2 != 0
                        }).map({ (_: Int, element: Int) -> Double in
                            return Double(element)
                        })
Cameron Lowell Palmer
sumber
9

Menggunakan .enumerate()karya, tetapi tidak memberikan indeks sebenarnya dari elemen; itu hanya memberikan Int yang diawali dengan 0 dan bertambah 1 untuk setiap elemen berurutan. Ini biasanya tidak relevan, tetapi ada potensi untuk perilaku yang tidak terduga ketika digunakan dengan ArraySlicetipe tersebut. Ambil kode berikut:

let a = ["a", "b", "c", "d", "e"]
a.indices //=> 0..<5

let aSlice = a[1..<4] //=> ArraySlice with ["b", "c", "d"]
aSlice.indices //=> 1..<4

var test = [Int: String]()
for (index, element) in aSlice.enumerate() {
    test[index] = element
}
test //=> [0: "b", 1: "c", 2: "d"] // indices presented as 0..<3, but they are actually 1..<4
test[0] == aSlice[0] // ERROR: out of bounds

Ini adalah contoh yang agak dibuat-buat, dan itu bukan masalah umum dalam praktiknya, tetapi saya pikir tetap berharga mengetahui hal ini bisa terjadi.

Cole Campbell
sumber
2
it does not actually provide the true index of the element; it only provides an Int beginning with 0 and incrementing by 1 for each successive elementYa, itu sebabnya ini disebut enumerate . Selain itu, slice bukan array, jadi tidak mengherankan jika berperilaku berbeda. Tidak ada bug di sini - semuanya sesuai desain. :)
Eric Aya
5
Benar, tapi saya tidak pernah menyebutnya bug. Itu hanya berpotensi perilaku tak terduga yang menurut saya layak disebutkan bagi mereka yang tidak tahu bagaimana itu bisa berinteraksi secara negatif dengan tipe ArraySlice.
Cole Campbell
Apakah Anda mengetahui cara untuk mendapatkan indeks elemen yang sebenarnya - misalnya jika menggunakan filterterlebih dahulu?
Alexandre Cassagne
9

Dimulai dengan Swift 3, itu

for (index, element) in list.enumerated() {
  print("Item \(index): \(element)")
}
Jake Lin
sumber
9

Untuk kelengkapan Anda dapat dengan mudah beralih pada indeks array Anda dan menggunakan subskrip untuk mengakses elemen pada indeks yang sesuai:

let list = [100,200,300,400,500]
for index in list.indices {
    print("Element at:", index, " Value:", list[index])
}

Menggunakan forEach

list.indices.forEach {
    print("Element at:", $0, " Value:", list[$0])
}

Menggunakan enumerated()metode pengumpulan . Perhatikan bahwa ia mengembalikan koleksi tupel dengan offsetdan element:

for item in list.enumerated() {
    print("Element at:", item.offset, " Value:", item.element)
}

menggunakan forEach:

list.enumerated().forEach {
    print("Element at:", $0.offset, " Value:", $0.element)
}

Itu akan dicetak

Elemen pada: 0 Nilai: 100

Elemen pada: 1 Nilai: 200

Elemen pada: 2 Nilai: 300

Elemen pada: 3 Nilai: 400

Elemen pada: 4 Nilai: 500

Jika Anda membutuhkan indeks array (bukan offset) dan elemennya, Anda dapat memperluas Koleksi dan membuat metode Anda sendiri untuk mendapatkan elemen yang diindeks:

extension Collection {
    func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
        var index = startIndex
        for element in self {
            try body((index,element))
            formIndex(after: &index)
        }
    }
}

Kemungkinan implementasi lain seperti yang disarankan oleh Alex adalah meng-zip indeks koleksi dengan elemen-elemennya:

extension Collection {
    func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
        for element in zip(indices, self) { try body(element) }
    }
}

Pengujian:

let list =  ["100","200","300","400","500"]
list.dropFirst(2).indexedElements {
    print("Index:", $0.index, "Element:", $0.element)
}

Ini akan p [rintis

Indeks: 2 Elemen: 300

Indeks: 3 Elemen: 400

Indeks: 4 Elemen: 500

Leo Dabus
sumber
BTW Anda dapat menerapkan enumeratedIndicesdengan looping over denganzip(self.indices, self)
Alexander - Reinstate Monica
@ Alexander-ReinstateMonica for element in zip(indices, self) { try body(element) }. Tapi saya tidak suka penamaan yang saya pilih, indexedElementsmungkin menggambarkan lebih baik apa yang dilakukannya
Leo Dabus
Ooo saya pikir itu nama yang lebih baik. Ya, satu forloop bekerja, tetapi jugazip(self.indices, self) .forEach(body)
Alexander - Reinstate Monica
@ Alexander-ReinstateMonica forEachmelakukan perulangan untuk di belakang layar. Saya lebih suka menjaganya agar tetap sederhana github.com/apple/swift/blob/master/stdlib/public/core/… @inlinable public func forEach( _ body: (Element) throws -> Void ) rethrows { for element in self { try body(element) } } }
Leo Dabus
7

Ini adalah rumus loop Enumeration:

for (index, value) in shoppingList.enumerate() {
print("Item \(index + 1): \(value)")
}

untuk detail lebih lanjut Anda dapat memeriksa Di Sini .

Dara Tith
sumber
5

Saya pribadi lebih suka menggunakan forEachmetode ini:

list.enumerated().forEach { (index, element) in
    ...
}

Anda juga dapat menggunakan versi singkat:

list.enumerated().forEach { print("index: \($0.0), value: \($0.1)") }
davebcn87
sumber
4

Xcode 8 dan Swift 3: Array dapat dihitung menggunakan tempArray.enumerated()

Contoh:

var someStrs = [String]()

someStrs.append("Apple")  
someStrs.append("Amazon")  
someStrs += ["Google"]    


for (index, item) in someStrs.enumerated()  
{  
        print("Value at index = \(index) is \(item)").  
}

menghibur:

Value at index = 0 is Apple
Value at index = 1 is Amazon
Value at index = 2 is Google
Anil Gupta
sumber
3

Bagi yang ingin menggunakan forEach .

Cepat 4

extension Array {
  func forEachWithIndex(_ body: (Int, Element) throws -> Void) rethrows {
    try zip((startIndex ..< endIndex), self).forEach(body)
  }
}

Atau

array.enumerated().forEach { ... }
Vincent Sit
sumber
2

Untuk apa yang ingin Anda lakukan, Anda harus menggunakan enumerated()metode pada Array Anda :

for (index, element) in list.enumerated() {
    print("\(index) - \(element)")
}
Ale Mohamad
sumber
-1

Kami memanggil fungsi enumerate untuk mengimplementasikan ini. Suka

untuk (indeks, elemen) di array.enumerate () {

// index adalah indexposition of array

// elemen adalah elemen array

}

Rakesh Kunwar
sumber