Come ripetere un ciclo con indice ed elemento in Swift


784

Esiste una funzione che posso usare per scorrere su un array e avere sia index che element, come l'enumerato di Python?

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

Risposte:


1662

Sì. A partire da Swift 3.0, se è necessario l'indice per ciascun elemento insieme al suo valore, è possibile utilizzare il enumerated()metodo per scorrere sull'array. Restituisce una sequenza di coppie composta dall'indice e dal valore per ciascun elemento dell'array. Per esempio:

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

Prima di Swift 3.0 e dopo Swift 2.0, la funzione era chiamata enumerate():

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

Prima di Swift 2.0, enumerateera una funzione globale.

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

3
Sebbene sembri una tupla, in Swift 1.2 - non sicuro di 2.0 - enumerate restituisce una struttura EnumerateSequence <base: SequenceType>.
nstein,

2
@Leviathlon in termini di prestazioni evidenti o misurabili che conta? No.
Dan Rosenstark

ottenere l'accesso all'indice è l'unico vantaggio dell'utilizzo enumerate?
Miele,

29
Forse cambieranno in gerundio, enumeratingper Swift 4. Emozionante!
Dan Rosenstark,

3
@Honey for (index, element) inquando si usa enumeratedè fuorviante. dovrebbe esserefor (offset, element) in
Leo Dabus

116

Swift 5 fornisce un metodo chiamato enumerated()per Array. enumerated()ha la seguente dichiarazione:

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

Restituisce una sequenza di coppie (n, x), dove n rappresenta un numero intero consecutivo a partire da zero e x rappresenta un elemento della sequenza.


Nei casi più semplici, è possibile utilizzare enumerated()con un ciclo for. Per esempio:

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

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

Nota comunque che non sei limitato all'uso enumerated()con un ciclo for. In effetti, se prevedi di utilizzare enumerated()un ciclo for per qualcosa di simile al seguente codice, stai sbagliando:

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

Un modo più veloce per farlo è:

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

In alternativa, puoi anche utilizzare enumerated()con 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]]

Inoltre, sebbene abbia alcune limitazioni , forEachpuò essere un buon sostituto di un ciclo for:

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

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

Usando enumerated()e makeIterator(), puoi anche iterare manualmente sul tuo Array. Per esempio:

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
 */

1
Ottenere l'accesso all'indice è l'unico vantaggio dell'utilizzo enumerate?
Miele

52

A partire da Swift 2, la funzione enumerata deve essere chiamata sulla raccolta in questo modo:

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

47

Ho trovato questa risposta mentre cercavo un modo per farlo con un dizionario , e risulta abbastanza facile adattarlo, basta passare una tupla per l'elemento.

// Swift 2

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

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

18

Puoi semplicemente usare il ciclo di enumerazione per ottenere il risultato desiderato:

Swift 2:

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

Swift 3 e 4:

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

Oppure puoi semplicemente passare attraverso un ciclo for per ottenere lo stesso risultato:

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

Spero che sia d'aiuto.


14

Elenco di base

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

o con Swift 3 ...

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

Enumera, filtra e mappa

Tuttavia, il più delle volte uso enumerate in combinazione con mappa o filtro. Ad esempio, operando su un paio di array.

In questo array volevo filtrare elementi pari o dispari e convertirli da Ints a Doppio. Quindi enumerate()ottieni l'indice e l'elemento, quindi il filtro controlla l'indice e, infine, per sbarazzarmi della tupla risultante, la mappa solo sull'elemento.

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

9

Utilizzando .enumerate()funziona, ma non fornisce il vero indice dell'elemento; fornisce solo un Int che inizia con 0 e aumenta di 1 per ogni elemento successivo. Questo di solito è irrilevante, ma esiste il potenziale per un comportamento imprevisto se utilizzato con il ArraySlicetipo. Prendi il seguente codice:

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

È un esempio un po 'inventato, e in pratica non è un problema comune, ma penso comunque che valga la pena sapere che ciò può accadere.


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 elementSì, ecco perché si chiama enumerate . Inoltre, slice non è un array, quindi nessuna sorpresa si comporta diversamente. Non c'è nessun bug qui - tutto è di progettazione. :)
Eric Aya,

5
Vero, ma non l'ho mai definito un bug. È solo un comportamento potenzialmente inaspettato che ho pensato che valesse la pena menzionare per coloro che non sapevano come interagire negativamente con il tipo ArraySlice.
Cole Campbell,

Sei a conoscenza di un modo per ottenere l'indice dell'elemento effettivo, ad esempio se lo usi per filterprimo?
Alexandre Cassagne,

9

A partire da Swift 3, lo è

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

9

Per completezza, puoi semplicemente scorrere gli indici dell'array e utilizzare il pedice per accedere all'elemento nell'indice corrispondente:

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

Usando forOach

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

Utilizzando il enumerated()metodo di raccolta . Nota che restituisce una raccolta di tuple con il offsete il element:

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

usando forOach:

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

Quelli stamperanno

Elemento a: 0 Valore: 100

Elemento a: 1 Valore: 200

Elemento a: 2 Valore: 300

Elemento a: 3 Valore: 400

Elemento a: 4 Valore: 500

Se hai bisogno dell'indice di array (non dell'offset) e del suo elemento, puoi estendere Collection e creare il tuo metodo per ottenere gli elementi indicizzati:

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

Un'altra possibile implementazione suggerita da Alex è comprimere gli indici di raccolta con i suoi elementi:

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

test:

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

Questo p [rint

Indice: 2 Elemento: 300

Indice: 3 Elemento: 400

Indice: 4 Elemento: 500


A proposito, puoi implementarlo enumeratedIndicesripetendo ciclicamente conzip(self.indices, self)
Alexander - Ripristina Monica il

@ Alexander-ReinstateMonica for element in zip(indices, self) { try body(element) }. A proposito, non mi piace il nome che ho scelto, indexedElementspotrebbe descrivere meglio cosa fa
Leo Dabus,

Ooo penso che sia un nome migliore. Sì, funziona un forciclo, ma anchezip(self.indices, self) .forEach(body)
Alexander - Ripristina Monica il

@ Alexander-ReinstateMonica forEachfa un ciclo for dietro le quinte. Preferisco mantenerlo semplice 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

Questa è la formula del ciclo di enumerazione:

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

per maggiori dettagli puoi controllare qui .


5

Personalmente preferisco usare il forEachmetodo:

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

Puoi anche usare la versione breve:

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

4

Xcode 8 e Swift 3: l'array può essere elencato usando tempArray.enumerated()

Esempio:

var someStrs = [String]()

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


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

console:

Value at index = 0 is Apple
Value at index = 1 is Amazon
Value at index = 2 is Google

3

Per coloro che vogliono usare forEach.

Swift 4

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

O

array.enumerated().forEach { ... }

2

Per ciò che si desidera fare, è necessario utilizzare il enumerated()metodo sull'array :

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

-1

Abbiamo chiamato la funzione di enumerazione per implementare questo. piace

per (indice, elemento) in array.enumerate () {

// index è indicizzazione dell'array

// element è element di array

}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.