Comment itérer une boucle avec index et élément dans Swift

784

Existe-t-il une fonction que je peux utiliser pour parcourir un tableau et avoir à la fois un index et un élément, comme l'énumération de Python?

for index, element in enumerate(list):
    ...
penseur3
la source

Réponses:

1662

Oui. À partir de Swift 3.0, si vous avez besoin de l'index pour chaque élément avec sa valeur, vous pouvez utiliser la enumerated()méthode pour parcourir le tableau. Il renvoie une séquence de paires composée de l'index et de la valeur de chaque élément du tableau. Par exemple:

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

Avant Swift 3.0 et après Swift 2.0, la fonction était appelée enumerate():

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

Avant Swift 2.0, enumerateétait une fonction mondiale.

for (index, element) in enumerate(list) {
    println("Item \(index): \(element)")
}
Cezar
la source
3
Bien que cela ressemble à un tuple, dans Swift 1.2 - pas sûr de 2.0 - enumerate renvoie une structure EnumerateSequence <base: SequenceType>.
nstein
2
@Leviathlon frais généraux de performance perceptibles ou mesurables qui importent? Non.
Dan Rosenstark
l'accès à l'index est-il le seul avantage de l'utilisation enumerate?
Honey
29
Peut-être qu'ils passeront au gérondif, enumeratingpour Swift 4. Excitant!
Dan Rosenstark
3
@Honey for (index, element) inlors de l'utilisation enumeratedest trompeur. devrait êtrefor (offset, element) in
Leo Dabus
116

Swift 5 fournit une méthode appelée enumerated()pour Array. enumerated()a la déclaration suivante:

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

Renvoie une séquence de paires (n, x), où n représente un entier consécutif commençant à zéro et x représente un élément de la séquence.


Dans les cas les plus simples, vous pouvez utiliser enumerated()une boucle for. Par exemple:

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

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

Notez cependant que vous n'êtes pas limité à une utilisation enumerated()avec une boucle for. En fait, si vous prévoyez d'utiliser enumerated()avec une boucle for pour quelque chose de similaire au code suivant, vous le faites mal:

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

Une façon plus rapide de le faire est:

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

Comme alternative, vous pouvez également utiliser enumerated() avec 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]]

De plus, bien qu’il ait limites , il forEachpeut être un bon remplacement d'une boucle for:

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

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

En utilisant enumerated()et makeIterator(), vous pouvez même itérer manuellement sur votre Array. Par exemple:

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
la source
1
L'accès à l'index est-il le seul avantage de l'utilisation enumerate?
Honey
52

À partir de Swift 2, la fonction énumération doit être appelée sur la collection comme suit:

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

J'ai trouvé cette réponse en cherchant un moyen de le faire avec un dictionnaire , et il s'avère qu'il est assez facile de l'adapter, il suffit de passer un tuple pour l'élément.

// Swift 2

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

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

Vous pouvez simplement utiliser une boucle d'énumération pour obtenir le résultat souhaité:

Swift 2:

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

Swift 3 & 4:

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

Ou vous pouvez simplement passer par une boucle for pour obtenir le même résultat:

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

J'espère que cela aide.

Riajur Rahman
la source
14

Énumération de base

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

ou avec Swift 3 ...

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

Énumérer, filtrer et mapper

Cependant, j'utilise le plus souvent enumerate en combinaison avec map ou filter. Par exemple, en opérant sur quelques tableaux.

Dans ce tableau, je voulais filtrer les éléments impairs ou même indexés et les convertir d'Ints en Doubles. Ainsi, enumerate()vous obtenez l'index et l'élément, puis le filtre vérifie l'index, et enfin, pour me débarrasser du tuple résultant, je le mappe uniquement sur l'élément.

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
la source
9

Utiliser des .enumerate()œuvres, mais il ne fournit pas le véritable index de l'élément; il ne fournit qu'un Int commençant par 0 et incrémenté de 1 pour chaque élément successif. Cela n'est généralement pas pertinent, mais il existe un risque de comportement inattendu lorsqu'il est utilisé avec le ArraySlicetype. Prenez le code suivant:

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

C'est un exemple quelque peu artificiel, et ce n'est pas un problème courant dans la pratique, mais je pense que cela vaut la peine de savoir que cela peut se produire.

Cole Campbell
la source
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 elementOui, c'est pourquoi cela s'appelle énumérer . De plus, la tranche n'est pas un tableau, donc pas surprenant qu'elle se comporte différemment. Il n'y a pas de bug ici - tout est par conception. :)
Eric Aya
5
C'est vrai, mais je ne l'ai jamais appelé un bug. C'est juste un comportement potentiellement inattendu que je pensais mériter d'être mentionné pour ceux qui ne savaient pas comment il pouvait interagir négativement avec le type ArraySlice.
Cole Campbell
Connaissez-vous un moyen d'obtenir l'index de l'élément réel - par exemple si vous l'utilisez en filterpremier?
Alexandre Cassagne
9

A partir de Swift 3, c'est

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

Pour être complet, vous pouvez simplement parcourir vos indices de tableau et utiliser l'indice pour accéder à l'élément à l'index correspondant:

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

Utilisation de forEach

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

Utilisation de la enumerated()méthode de collecte . Notez qu'il retourne une collection de tuples avec le offsetet le element:

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

en utilisant forEach:

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

Ceux-ci imprimeront

Élément à: 0 Valeur: 100

Élément à: 1 Valeur: 200

Élément à: 2 Valeur: 300

Élément à: 3 Valeur: 400

Élément à: 4 Valeur: 500

Si vous avez besoin de l'index du tableau (pas du décalage) et de son élément, vous pouvez étendre Collection et créer votre propre méthode pour obtenir les éléments indexés:

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

Une autre implémentation possible comme suggéré par Alex est de compresser les index de collection avec ses éléments:

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

Essai:

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

Ce sera p [rint

Index: 2 Élément: 300

Index: 3 Élément: 400

Index: 4 Élément: 500

Leo Dabus
la source
BTW vous pouvez implémenter enumeratedIndicesen bouclant aveczip(self.indices, self)
Alexander - Reinstate Monica
@ Alexander-ReinstateMonica for element in zip(indices, self) { try body(element) }. Btw je n'aime pas le nom que j'ai choisi, indexedElementspourrait mieux décrire ce qu'il fait
Leo Dabus
Ooo je pense que c'est un meilleur nom. Oui, une forboucle fonctionne, mais aussizip(self.indices, self) .forEach(body)
Alexander - Reinstate Monica
@ Alexander-ReinstateMonica forEachfait une boucle for dans les coulisses. Je préfère rester simple et simple 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

C'est la formule de boucle d'énumération:

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

pour plus de détails, vous pouvez vérifier ici .

Dara Tith
la source
5

Personnellement, je préfère utiliser la forEachméthode:

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

Vous pouvez également utiliser la version courte:

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

Xcode 8 et Swift 3: les tableaux peuvent être énumérés à l'aide de tempArray.enumerated()

Exemple:

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
Anil Gupta
la source
3

Pour ceux qui veulent utiliser forEach.

Swift 4

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

Ou

array.enumerated().forEach { ... }
Vincent Sit
la source
2

Pour ce que vous voulez faire, vous devez utiliser la enumerated()méthode sur votre tableau :

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

Nous avons appelé la fonction énumération pour implémenter ceci. comme

for (index, element) dans array.enumerate () {

// index est la position d'index du tableau

// l'élément est l'élément du tableau

}

Rakesh Kunwar
la source