So iterieren Sie eine Schleife mit Index und Element in Swift

784

Gibt es eine Funktion, mit der ich über ein Array iterieren und sowohl Index als auch Element haben kann, wie z. B. Pythons Aufzählung?

for index, element in enumerate(list):
    ...
thinker3
quelle

Antworten:

1661

Ja. Ab Swift 3.0 können Sie die enumerated()Methode verwenden , um über das Array zu iterieren , wenn Sie den Index für jedes Element zusammen mit seinem Wert benötigen . Es gibt eine Folge von Paaren zurück, die aus dem Index und dem Wert für jedes Element im Array bestehen. Zum Beispiel:

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

Vor Swift 3.0 und nach Swift 2.0 wurde die Funktion aufgerufen enumerate():

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

Vor Swift 2.0 enumeratewar eine globale Funktion.

for (index, element) in enumerate(list) {
    println("Item \(index): \(element)")
}
Cezar
quelle
3
Obwohl es wie ein Tupel erscheint, gibt enumerate in Swift 1.2 - nicht sicher über 2.0 - eine EnumerateSequence <base: SequenceType> -Struktur zurück.
Nstein
2
@ Leviathlon spürbarer oder messbarer Leistungsaufwand, der wichtig sein wird? Nein.
Dan Rosenstark
Ist der Zugriff auf den Index der einzige Vorteil der Verwendung enumerate?
Honig
29
Vielleicht wechseln sie zum Gerundium, enumerating für Swift 4 Aufregend!
Dan Rosenstark
3
@Honey for (index, element) inbei der Verwendung enumeratedist irreführend. sollte seinfor (offset, element) in
Leo Dabus
116

Swift 5 stellt ein Verfahren genannt enumerated()für Array. enumerated()hat die folgende Erklärung:

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

Gibt eine Folge von Paaren (n, x) zurück, wobei n eine aufeinanderfolgende ganze Zahl ab Null darstellt und x ein Element der Folge darstellt.


In den einfachsten Fällen können Sie enumerated()eine for-Schleife verwenden. Zum Beispiel:

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

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

Beachten Sie jedoch, dass Sie nicht auf die Verwendung enumerated()mit einer for-Schleife beschränkt sind. Wenn Sie vorhaben, enumerated()eine for-Schleife für etwas zu verwenden, das dem folgenden Code ähnelt, machen Sie es falsch:

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

Ein schnellerer Weg, dies zu tun, ist:

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

Alternativ können Sie auch verwenden enumerated() mit 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]]

Darüber hinaus hat es zwar einige Einschränkungen , forEachkann es außerdem ein guter Ersatz für eine for-Schleife sein:

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

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

Mit enumerated()und makeIterator()können Sie sogar manuell auf Ihrem Computer iterieren Array. Zum Beispiel:

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
quelle
1
Ist der Zugriff auf den Index der einzige Vorteil der Verwendung enumerate?
Honig
52

Ab Swift 2 muss die Aufzählungsfunktion für die Sammlung wie folgt aufgerufen werden:

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

Ich habe diese Antwort gefunden, als ich nach einer Möglichkeit gesucht habe, dies mit einem Wörterbuch zu tun , und es stellt sich heraus, dass es ziemlich einfach ist, sie anzupassen. Übergeben Sie einfach ein Tupel für das Element.

// Swift 2

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

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

Sie können einfach eine Aufzählungsschleife verwenden, um das gewünschte Ergebnis zu erzielen:

Swift 2:

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

Swift 3 & 4:

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

Oder Sie können einfach eine for-Schleife durchlaufen, um das gleiche Ergebnis zu erhalten:

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

Ich hoffe es hilft.

Riajur Rahman
quelle
14

Grundlegende Aufzählung

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

oder mit Swift 3 ...

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

Aufzählen, Filtern und Zuordnen

Am häufigsten verwende ich jedoch Aufzählung in Kombination mit Karte oder Filter. Zum Beispiel mit der Bearbeitung einiger Arrays.

In diesem Array wollte ich ungerade oder gerade indizierte Elemente filtern und sie von Ints in Doubles konvertieren. So enumerate()bekommt man Index und das Element, dann Filter prüfen der Index, und schließlich den resultierenden Tupel ich es nur das Element Karte loszuwerden.

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
quelle
9

Verwenden von .enumerate()Werken, liefert jedoch nicht den wahren Index des Elements. Es wird nur ein Int bereitgestellt, das mit 0 beginnt und für jedes aufeinanderfolgende Element um 1 erhöht wird. Dies ist normalerweise irrelevant, aber es besteht die Möglichkeit eines unerwarteten Verhaltens, wenn es mit dem ArraySliceTyp verwendet wird. Nehmen Sie den folgenden Code:

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

Es ist ein etwas erfundenes Beispiel, und es ist in der Praxis kein alltägliches Problem, aber ich denke, es lohnt sich zu wissen, dass dies passieren kann.

Cole Campbell
quelle
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 elementJa, deshalb heißt es Aufzählung . Außerdem ist Slice kein Array, daher ist es keine Überraschung, dass es sich anders verhält. Hier gibt es keinen Fehler - alles ist beabsichtigt. :)
Eric Aya
5
Stimmt, aber ich habe es nie einen Fehler genannt. Es ist nur ein potenziell unerwartetes Verhalten, das meiner Meinung nach für diejenigen erwähnenswert ist, die nicht wussten, wie es sich negativ auf den ArraySlice-Typ auswirken kann.
Cole Campbell
Kennen Sie eine Möglichkeit, den Index des tatsächlichen Elements abzurufen - zum Beispiel, wenn Sie filterzuerst verwenden?
Alexandre Cassagne
9

Beginnend mit Swift 3 ist es

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

Der Vollständigkeit halber können Sie einfach Ihre Array-Indizes durchlaufen und mithilfe des Index auf das Element am entsprechenden Index zugreifen:

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

ForEach verwenden

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

Sammlungsmethode enumerated()verwenden. Beachten Sie, dass eine Sammlung von Tupeln mit offsetund zurückgegeben wird element:

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

mit forEach:

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

Diese werden gedruckt

Element bei: 0 Wert: 100

Element bei: 1 Wert: 200

Element bei: 2 Wert: 300

Element bei: 3 Wert: 400

Element bei: 4 Wert: 500

Wenn Sie den Array-Index (nicht den Offset) und sein Element benötigen, können Sie Collection erweitern und eine eigene Methode erstellen, um die indizierten Elemente abzurufen:

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

Eine andere mögliche Implementierung, wie von Alex vorgeschlagen, besteht darin, die Sammlungsindizes mit ihren Elementen zu komprimieren:

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

Testen:

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

Dies wird gedruckt

Index: 2 Element: 300

Index: 3 Element: 400

Index: 4 Element: 500

Leo Dabus
quelle
Übrigens können Sie implementieren, enumeratedIndicesindem Sie mitzip(self.indices, self)
Alexander - Reinstate Monica
@ Alexander-ReinstateMonica for element in zip(indices, self) { try body(element) }. Übrigens mag ich die von mir indexedElements
gewählte
Ooo ich denke das ist ein besserer Name. Ja, eine forSchleife funktioniert, aber auchzip(self.indices, self) .forEach(body)
Alexander - stellt Monica
@ Alexander-ReinstateMonica forEachmacht eine for-Schleife hinter den Kulissen. Ich ziehe es vor, es einfach zu halten 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

Dies ist die Formel der Aufzählungsschleife:

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

Weitere Details finden Sie hier .

Dara Tith
quelle
5

Ich persönlich bevorzuge die forEachMethode:

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

Sie können auch die Kurzversion verwenden:

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

Xcode 8 und Swift 3: Array können mit aufgelistet werden tempArray.enumerated()

Beispiel:

var someStrs = [String]()

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


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

Konsole:

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

Für diejenigen, die verwenden möchten forEach.

Swift 4

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

Oder

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

Für das, was Sie tun möchten, sollten Sie die enumerated()Methode auf Ihrem Array verwenden :

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

Wir haben die Aufzählungsfunktion aufgerufen, um dies zu implementieren. mögen

für (Index, Element) in array.enumerate () {

// Index ist die Indexposition des Arrays

// Element ist Element des Arrays

}}

Rakesh Kunwar
quelle