Comment trouver l'index d'un élément de liste dans Swift?

443

J'essaie de trouver un item indexen recherchant un list. Est-ce que quelqu'un sait comment faire ça?

Je vois qu'il y en a list.StartIndexet list.EndIndexje veux quelque chose comme le python list.index("text").

Chéyo
la source

Réponses:

824

Comme swift est à certains égards plus fonctionnel qu'orienté objet (et les tableaux sont des structures, pas des objets), utilisez la fonction "find" pour opérer sur le tableau, qui retourne une valeur facultative, alors soyez prêt à gérer une valeur nulle:

let arr:Array = ["a","b","c"]
find(arr, "c")!              // 2
find(arr, "d")               // nil

Mise à jour pour Swift 2.0:

L'ancienne findfonction n'est plus supportée avec Swift 2.0!

Avec Swift 2.0, Arraygagne la capacité de trouver l'index d'un élément en utilisant une fonction définie dans une extension de CollectionType(qui Arrayimplémente):

let arr = ["a","b","c"]

let indexOfA = arr.indexOf("a") // 0
let indexOfB = arr.indexOf("b") // 1
let indexOfD = arr.indexOf("d") // nil

De plus, la recherche du premier élément d'un tableau remplissant un prédicat est prise en charge par une autre extension de CollectionType:

let arr2 = [1,2,3,4,5,6,7,8,9,10]
let indexOfFirstGreaterThanFive = arr2.indexOf({$0 > 5}) // 5
let indexOfFirstGreaterThanOneHundred = arr2.indexOf({$0 > 100}) // nil

Notez que ces deux fonctions renvoient des valeurs facultatives, comme findprécédemment.

Mise à jour pour Swift 3.0:

Notez que la syntaxe de indexOf a changé. Pour les articles conformes à Equatablevous pouvez utiliser:

let indexOfA = arr.index(of: "a")

Une documentation détaillée de la méthode peut être trouvée à https://developer.apple.com/reference/swift/array/1689674-index

Pour les éléments de tableau non conformes, Equatablevous devrez utiliser index(where:):

let index = cells.index(where: { (item) -> Bool in
  item.foo == 42 // test if this is the item you're looking for
})

Mise à jour pour Swift 4.2:

Avec Swift 4.2, indexn'est plus utilisé mais est séparé en firstIndexet lastIndexpour une meilleure clarification. Donc, selon que vous recherchez le premier ou le dernier index de l'article:

let arr = ["a","b","c","a"]

let indexOfA = arr.firstIndex(of: "a") // 0
let indexOfB = arr.lastIndex(of: "a") // 3
Sebastian Schuth
la source
22
Où avez-vous découvert la fonction de recherche? Je n'arrive pas à trouver de documentation sur "find" ou toute autre fonction globale
Johannes
14
Venant du monde de la POO, comment suis-je censé trouver ce genre de fonctions flottantes libres?
Rudolf Adamkovič, le
4
Ils semblent être largement sans papiers. Voir pratiqueswift.com/2014/06/14/… pour une liste (mais sachez que je n'ai pas vérifié si la liste est complète ou à jour).
Sebastian Schuth,
5
Johannes et @Rudolf - utilisez Dash.app. C'est une application OS X pour parcourir la documentation. Il a une référence de langage pour Swift qui a une liste de toutes les fonctions flottantes libres. Facile à filtrer et à rechercher. Je ne peux pas vivre sans ça.
Bjorn
4
FYI: Si vous utilisez indexOfsur un tableau de structures que vous avez défini vous-même, votre structure doit respecter le Equatableprotocole.
Matthew Quiros
202

tl; dr:

Pour les cours, vous cherchez peut-être:

let index = someArray.firstIndex{$0 === someObject}

Réponse complète:

Je pense qu'il vaut la peine de mentionner qu'avec les types de référence ( class), vous voudrez peut-être effectuer une comparaison d' identité , auquel cas il vous suffit d'utiliser l' ===opérateur d'identité dans la fermeture du prédicat:


Swift 5, Swift 4.2:

let person1 = Person(name: "John")
let person2 = Person(name: "Sue")
let person3 = Person(name: "Maria")
let person4 = Person(name: "Loner")

let people = [person1, person2, person3]

let indexOfPerson1 = people.firstIndex{$0 === person1} // 0
let indexOfPerson2 = people.firstIndex{$0 === person2} // 1
let indexOfPerson3 = people.firstIndex{$0 === person3} // 2
let indexOfPerson4 = people.firstIndex{$0 === person4} // nil

Notez que la syntaxe ci-dessus utilise la syntaxe des fermetures finales et équivaut à:

let indexOfPerson1 = people.firstIndex(where: {$0 === person1})


Swift 4 / Swift 3 - la fonction qui s'appelait auparavant index

Swift 2 - la fonction qui s'appelait autrefois indexOf

* Notez le commentaire pertinent et utile de paulbailey sur les classtypes qui implémentent Equatable, où vous devez déterminer si vous devez comparer en utilisant ===( opérateur d' identité ) ou ==( opérateur d' égalité ). Si vous décidez de faire correspondre l'utilisation ==, vous pouvez simplement utiliser la méthode suggérée par les autres ( people.firstIndex(of: person1)).

Nikolay Suvandzhiev
la source
6
Ceci est un article utile pour ceux qui se demandent pourquoi .indexOf (x) ne fonctionne pas - cette solution est inattendue mais parfaitement évidente rétrospectivement.
Maury Markowitz
1
Merci beaucoup pour cela mais ce n'est pas du tout évident pour moi. J'ai regardé la documentation et je ne comprends vraiment pas pourquoi aurais-je besoin d'une fermeture de prédicat lorsque j'utilise indexOf sur un type de référence? Il semble qu'indexOf devrait déjà être capable de gérer les types de référence par lui-même. Il doit savoir qu'il s'agit d'un type de référence et non d'un type de valeur.
Chris
7
Si Personimplémenté le Equatableprotocole, cela ne serait pas nécessaire.
paulbailey
2
Je reçois: Binary operator '===' cannot be applied to operands of type '_' and 'Post', Postest mon struct ... une idée?
David Seek
@DavidSeek, structs(et enums) sont des types de valeur, pas des types de référence. Seuls les types de référence (par exemple class) ont une logique de comparaison d'identité ( ===). Consultez les autres réponses pour savoir quoi faire structs(en gros, vous utilisez simplement le array.index(of: myStruct), en vous assurant que le type de myStructest conforme à Equatable( ==)).
Nikolay Suvandzhiev
81

Vous pouvez filterun tableau avec une fermeture:

var myList = [1, 2, 3, 4]
var filtered = myList.filter { $0 == 3 }  // <= returns [3]

Et vous pouvez compter un tableau:

filtered.count // <= returns 1

Vous pouvez donc déterminer si un tableau inclut votre élément en combinant ces éléments:

myList.filter { $0 == 3 }.count > 0  // <= returns true if the array includes 3

Si vous voulez trouver le poste, je ne vois pas de façon fantaisiste, mais vous pouvez certainement le faire comme ceci:

var found: Int?  // <= will hold the index if it was found, or else will be nil
for i in (0..x.count) {
    if x[i] == 3 {
        found = i
    }
}

ÉDITER

Pendant que nous y sommes, pour un exercice amusant, développons Arrayune findméthode:

extension Array {
    func find(includedElement: T -> Bool) -> Int? {
        for (idx, element) in enumerate(self) {
            if includedElement(element) {
                return idx
            }
        }
        return nil
    }
}

Maintenant, nous pouvons le faire:

myList.find { $0 == 3 }
// returns the index position of 3 or nil if not found
gwcoffey
la source
Pour les funsies, j'ai ajouté un autre exemple où j'étends le builtin Arraypour avoir une findméthode qui fait ce que vous voulez. Je ne sais pas encore si c'est une bonne pratique, mais c'est une expérience intéressante.
gwcoffey
2
Je veux juste souligner que vous devez utiliser ++ idx selon la documentation: "À moins que vous n'ayez besoin du comportement spécifique d'i ++, il est recommandé d'utiliser ++ i et --i dans tous les cas, car ils ont le comportement de modification de i et de retour du résultat. "
Logan
Bon appel. Pendant que vous publiez ce document, je le révisais pour l'utiliser enumerateafin qu'il ne s'applique plus, mais vous avez absolument raison.
gwcoffey
Oui, je me souvenais l'avoir remarqué dans l'un des exemples lorsque je traversais. J'essaie de prendre l'habitude maintenant :)
Logan
4
Pour que cela fonctionne sous Swift 2 / XCode 7, vous devez le modifier comme suit. Remplacez (includedElement: T -> Bool) par (includedElement: Element -> Bool) et changez enumerate (self) en self.enumerate
Scooter
35

Swift 5

func firstIndex(of element: Element) -> Int?

var alphabets = ["A", "B", "E", "D"]

Exemple 1

let index = alphabets.firstIndex(where: {$0 == "A"})

Exemple2

if let i = alphabets.firstIndex(of: "E") {
    alphabets[i] = "C" // i is the index
}
print(alphabets)
// Prints "["A", "B", "C", "D"]"
Saranjith
la source
25

Bien qu'il indexOf()fonctionne parfaitement, il ne renvoie qu'un seul index.

Je cherchais un moyen élégant d'obtenir un tableau d'index pour des éléments qui satisfont à certaines conditions.

Voici comment cela peut être fait:

Swift 3:

let array = ["apple", "dog", "log"]

let indexes = array.enumerated().filter {
    $0.element.contains("og")
    }.map{$0.offset}

print(indexes)

Swift 2:

let array = ["apple", "dog", "log"]

let indexes = array.enumerate().filter {
    $0.element.containsString("og")
    }.map{$0.index}

print(indexes)
Serhii Yakovenko
la source
12

Pour la classe personnalisée, vous devez implémenter le protocole Equatable.

import Foundation

func ==(l: MyClass, r: MyClass) -> Bool {
  return l.id == r.id
}

class MyClass: Equtable {
    init(id: String) {
        self.msgID = id
    }

    let msgID: String
}

let item = MyClass(3)
let itemList = [MyClass(1), MyClass(2), item]
let idx = itemList.indexOf(item)

printl(idx)
ZYiOS
la source
9

Mise à jour pour Swift 2:

sequence.contains (element) : Renvoie true si une séquence donnée (telle qu'un tableau) contient l'élément spécifié.

Swift 1:

Si vous cherchez simplement à vérifier si un élément est contenu dans un tableau, c'est-à-dire, obtenez simplement un indicateur booléen, utilisez à la contains(sequence, element)place de find(array, element):

contient (séquence, élément) : Renvoie vrai si une séquence donnée (comme un tableau) contient l'élément spécifié.

Voir l'exemple ci-dessous:

var languages = ["Swift", "Objective-C"]
contains(languages, "Swift") == true
contains(languages, "Java") == false
contains([29, 85, 42, 96, 75], 42) == true
if (contains(languages, "Swift")) {
  // Use contains in these cases, instead of find.   
}
Zorayr
la source
7

dans Swift 4.2

.index (où :) a été remplacé par .firstIndex (où :)

array.firstIndex(where: {$0 == "person1"})
Ridho Octanio
la source
6

Swift 4. Si votre tableau contient des éléments de type [String: AnyObject]. Donc, pour trouver l'index de l'élément, utilisez le code ci-dessous

var array = [[String: AnyObject]]()// Save your data in array
let objectAtZero = array[0] // get first object
let index = (self.array as NSArray).index(of: objectAtZero)

Ou Si vous souhaitez trouver l'index sur la base de la clé du dictionnaire. Ici, le tableau contient les objets de la classe Model et je fais correspondre la propriété id.

   let userId = 20
    if let index = array.index(where: { (dict) -> Bool in
           return dict.id == userId // Will found index of matched id
    }) {
    print("Index found")
    }
OR
      let storeId = Int(surveyCurrent.store_id) // Accessing model key value
      indexArrUpTo = self.arrEarnUpTo.index { Int($0.store_id) == storeId }! // Array contains models and finding specific one
Gurjinder Singh
la source
4

Swift 2.1

var array = ["0","1","2","3"]

if let index = array.indexOf("1") {
   array.removeAtIndex(index)
}

print(array) // ["0","2","3"]

Swift 3

var array = ["0","1","2","3"]

if let index = array.index(of: "1") {
    array.remove(at: index)
}
array.remove(at: 1)
Luca Davanzo
la source
3
vous mutez un let array? L'utilisation de selfest également discutable.
Chéyo
4

Dans Swift 4 , si vous parcourez votre tableau DataModel, assurez-vous que votre modèle de données est conforme au protocole Equatable, implémentez la méthode lhs = rhs, et alors seulement vous pouvez utiliser ".index (of". Par exemple

class Photo : Equatable{
    var imageURL: URL?
    init(imageURL: URL){
        self.imageURL = imageURL
    }

    static func == (lhs: Photo, rhs: Photo) -> Bool{
        return lhs.imageURL == rhs.imageURL
    }
}

Et alors,

let index = self.photos.index(of: aPhoto)
Naishta
la source
4

Dans Swift 2 (avec Xcode 7), Arrayinclut une indexOfméthode fournie par le CollectionTypeprotocole. (En fait, deux indexOfméthodes: une qui utilise l'égalité pour faire correspondre un argument et une autre qui utilise une fermeture.)

Avant Swift 2, il n'existait aucun moyen pour les types génériques comme les collections de fournir des méthodes pour les types concrets qui en dérivent (comme les tableaux). Donc, dans Swift 1.x, "index de" est une fonction globale ... Et il a également été renommé, donc dans Swift 1.x, cette fonction globale est appelée find.

Il est également possible (mais pas nécessaire) d'utiliser la indexOfObjectméthode de NSArray... ou l'une des autres méthodes de recherche plus sophistiquées de Foundation qui n'ont pas d'équivalents dans la bibliothèque standard de Swift. Juste import Foundation(ou un autre module qui importe transitoirement Foundation), transformez votre Arrayen NSArray, et vous pouvez utiliser les nombreuses méthodes de recherche sur NSArray.

rickster
la source
4

N'importe laquelle de cette solution fonctionne pour moi

Voici la solution que j'ai pour Swift 4:

let monday = Day(name: "M")
let tuesday = Day(name: "T")
let friday = Day(name: "F")

let days = [monday, tuesday, friday]

let index = days.index(where: { 
            //important to test with === to be sure it's the same object reference
            $0 === tuesday
        })
Kevin ABRIOUX
la source
2

Vous pouvez également utiliser la bibliothèque fonctionnelle Dollar pour faire un indexOf sur un tableau en tant que tel http://www.dollarswift.org/#indexof-indexof

$.indexOf([1, 2, 3, 1, 2, 3], value: 2) 
=> 1
Encore PTL
la source
2

Si vous travaillez toujours dans Swift 1.x

Alors essaye,

let testArray = ["A","B","C"]

let indexOfA = find(testArray, "A") 
let indexOfB = find(testArray, "B")
let indexOfC = find(testArray, "C")
iCyberPaul
la source
1

Pour SWIFT 3, vous pouvez utiliser une fonction simple

func find(objecToFind: String?) -> Int? {
   for i in 0...arrayName.count {
      if arrayName[i] == objectToFind {
         return i
      }
   }
return nil
}

Cela donnera la position du numéro, vous pouvez donc utiliser comme

arrayName.remove(at: (find(objecToFind))!)

J'espère être utile

Marco
la source
1

SWIFT 4

Supposons que vous souhaitiez stocker un numéro du tableau appelé cardButtons dans cardNumber, vous pouvez le faire de cette façon:

let cardNumber = cardButtons.index(of: sender)

l'expéditeur est le nom de votre bouton


la source
0

Swift 4

Pour les types de référence:

extension Array where Array.Element: AnyObject {

    func index(ofElement element: Element) -> Int? {
        for (currentIndex, currentElement) in self.enumerated() {
            if currentElement === element {
                return currentIndex
            }
        }
        return nil
    }
}
Menno
la source
0

Dans Swift 4/5, utilisez "firstIndex" pour trouver l'index.

let index = array.firstIndex{$0 == value}
Krunal Patel
la source
0

Si quelqu'un a ce problème

Cannot invoke initializer for type 'Int' with an argument list of type '(Array<Element>.Index?)'

je fais ça

extension Int {
    var toInt: Int {
        return self
    }
}

puis

guard let finalIndex = index?.toInt else {
    return false
}
Ahmed Safadi
la source
0

Pour (>= swift 4.0)

C'est assez simple. Considérez l' Arrayobjet suivant .

var names: [String] = ["jack", "rose", "jill"]

Pour obtenir l'index de l'élément rose, il vous suffit de:

names.index(of: "rose") // returns 1

Remarque:

  • Array.index(of:)renvoie un Optional<Int>.

  • nil implique que l'élément n'est pas présent dans le tableau.

  • Vous pourriez vouloir déballer de force la valeur retournée ou utiliser un if-letpour contourner l'option.

Mohammed Sadiq
la source
0

Utilisez simplement la méthode firstIndex.

array.firstIndex(where: { $0 == searchedItem })
erdikanik
la source