¿Cómo encontrar el índice del elemento de la lista en Swift?

443

Estoy tratando de encontrar un item indexmediante la búsqueda de a list. ¿Alguien sabe cómo hacer eso?

Veo que hay list.StartIndexy list.EndIndexquiero algo como Python list.index("text").

Chéyo
fuente

Respuestas:

823

Como swift es en algunos aspectos más funcional que orientado a objetos (y las matrices son estructuras, no objetos), use la función "find" para operar en la matriz, que devuelve un valor opcional, así que prepárese para manejar un valor nulo:

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

Actualización para Swift 2.0:

¡La findfunción anterior ya no es compatible con Swift 2.0!

Con Swift 2.0, Arraygana la capacidad de encontrar el índice de un elemento usando una función definida en una extensión de CollectionType(que Arrayimplementa):

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

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

Además, encontrar el primer elemento en una matriz que cumple un predicado es compatible con otra extensión 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

Tenga en cuenta que estas dos funciones devuelven valores opcionales, como lo findhizo antes.

Actualización para Swift 3.0:

Tenga en cuenta que la sintaxis de indexOf ha cambiado. Para los artículos que se ajustan a Equatableusted, puede usar:

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

Se puede encontrar una documentación detallada del método en https://developer.apple.com/reference/swift/array/1689674-index

Para los elementos de la matriz que no se ajustan Equatable, deberá usar index(where:):

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

Actualización para Swift 4.2:

Con Swift 4.2, indexya no se usa, pero se separa en firstIndexy lastIndexpara una mejor aclaración. Entonces, dependiendo de si está buscando el primer o el último índice del artículo:

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

let indexOfA = arr.firstIndex(of: "a") // 0
let indexOfB = arr.lastIndex(of: "a") // 3
Sebastian Schuth
fuente
22
¿Dónde aprendiste sobre la función de búsqueda? Parece que no puedo encontrar ninguna documentación de "find" o cualquier otra función global
Johannes
14
Viniendo del mundo OOP, ¿cómo se supone que voy a encontrar este tipo de funciones flotantes libres?
Rudolf Adamkovič
44
Parecen estar en gran parte indocumentados. Vea Practicawift.com/2014/06/14/… para obtener una lista (pero tenga en cuenta que no verifiqué si la lista está completa o actualizada).
Sebastian Schuth
55
Johannes y @Rudolf: use Dash.app. Es una aplicación OS X para navegar por la documentación. Tiene una referencia de lenguaje para Swift que tiene una lista de todas las funciones flotantes libres. Fácil de filtrar y buscar. No puedo vivir sin eso.
Bjorn
44
FYI: Si está utilizando indexOfuna matriz de estructuras que usted mismo definió, su estructura debe cumplir con el Equatableprotocolo.
Matthew Quiros
202

tl; dr:

Para las clases, podrías estar buscando:

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

Respuesta completa:

Creo que vale la pena mencionar que con los tipos de referencia ( class) es posible que desee realizar una comparación de identidad , en cuyo caso solo necesita usar el ===operador de identidad en el cierre del predicado:


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

Tenga en cuenta que la sintaxis anterior utiliza la sintaxis de cierre final y es equivalente a:

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


Swift 4 / Swift 3 - la función solía llamarse index

Swift 2 - la función solía llamarse indexOf

* Tenga en cuenta el comentario relevante y útil de paulbailey sobre los classtipos que implementan Equatable, donde debe considerar si debe comparar utilizando ===( operador de identidad ) o ==( operador de igualdad ). Si decide hacer coincidir el uso ==, simplemente puede usar el método sugerido por otros ( people.firstIndex(of: person1)).

Nikolay Suvandzhiev
fuente
66
Esta es una publicación útil para aquellos que se preguntan por qué .indexOf (x) no funciona: esta solución es inesperada pero perfectamente obvia en retrospectiva.
Maury Markowitz
1
Muchas gracias por esto, pero no es obvio para mí en absoluto. Miré la documentación y realmente no entiendo por qué necesitaría un cierre de predicado cuando uso indexOf en un tipo de referencia. Parece que indexOf ya debería poder manejar tipos de referencia por sí solo. Debe saber que es un tipo de referencia y no un tipo de valor.
Chris
77
Si se Personimplementa el Equatableprotocolo, esto no sería necesario.
Paulbailey
2
Estoy recibiendo: Binary operator '===' cannot be applied to operands of type '_' and 'Post', Postes mi struct ... alguna idea?
David Seek
@DavidSeek, structs(y enums) son tipos de valor, no tipos de referencia. Solo los tipos de referencia (p class. Ej. ) Tienen lógica de comparación de identidad ( ===). Echa un vistazo a las otras respuestas para saber qué hacer structs(básicamente, simplemente usas el array.index(of: myStruct), asegurándote de que el tipo se myStructajusta a Equatable( ==)).
Nikolay Suvandzhiev
81

Puede filteruna matriz con un cierre:

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

Y puedes contar una matriz:

filtered.count // <= returns 1

Para que pueda determinar si una matriz incluye su elemento combinando estos:

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

Si quieres encontrar la posición, no veo una manera elegante, pero ciertamente puedes hacerlo así:

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

EDITAR

Mientras estamos en eso, para un ejercicio divertido, extendamos Arraypara tener un findmétodo:

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

Ahora podemos hacer esto:

myList.find { $0 == 3 }
// returns the index position of 3 or nil if not found
gwcoffey
fuente
Para funsies, agregué otro ejemplo donde extiendo el incorporado Arraypara tener un findmétodo que haga lo que quieras. Todavía no sé si esta es una buena práctica, pero es un buen experimento.
gwcoffey
2
Solo quiero señalar que debe usar ++ idx según los documentos: "A menos que necesite el comportamiento específico de i ++, se recomienda que use ++ i y --i en todos los casos, porque tienen el típico esperado comportamiento de modificar i y devolver el resultado ".
Logan
Buena llamada. Mientras publicaba esto, lo estaba revisando enumeratepara que no se aplique, pero tiene toda la razón.
gwcoffey
Sí, recordé haberlo notado en uno de los ejemplos cuando estaba pasando. Tratando de ponerme en el hábito ahora :)
Logan
44
Para que esto funcione en Swift 2 / XCode 7, debe modificarlo de la siguiente manera. Reemplace (includedElement: T -> Bool) con (includedElement: Element -> Bool) y cambie enumerate (self) a self.enumerate
Scooter
35

Swift 5

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

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

Ejemplo 1

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

Ejemplo2

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

Si bien indexOf()funciona perfectamente, solo devuelve un índice.

Estaba buscando una forma elegante de obtener una variedad de índices para elementos que satisfagan alguna condición.

Así es como se puede hacer:

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
fuente
12

Para la clase personalizada, debe implementar el protocolo 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)
ZiOS
fuente
9

Actualización para Swift 2:

secuencia.contains (elemento) : Devuelve verdadero si una secuencia dada (como una matriz) contiene el elemento especificado.

Swift 1:

Si solo busca verificar si un elemento está contenido dentro de una matriz, es decir, solo obtenga un indicador booleano, use en contains(sequence, element)lugar de find(array, element):

contiene (secuencia, elemento) : devuelve verdadero si una secuencia dada (como una matriz) contiene el elemento especificado.

Ver ejemplo a continuación:

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
fuente
7

en Swift 4.2

.index (donde :) se cambió a .firstIndex (donde :)

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

Swift 4. Si su matriz contiene elementos de tipo [String: AnyObject]. Entonces, para encontrar el índice del elemento, use el siguiente código

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)

O bien, si desea encontrar el índice en función de la clave del diccionario. Aquí la matriz contiene objetos de la clase Modelo y estoy haciendo coincidir la propiedad 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
fuente
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
fuente
3
estás mutando a let array? El uso de selfes cuestionable también.
Chéyo
4

En Swift 4 , si está atravesando su matriz DataModel, asegúrese de que su modelo de datos cumpla con el Protocolo Equivalente, implemente el método lhs = rhs, y solo entonces puede usar ".index (de". Por ejemplo

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

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

Y entonces,

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

En Swift 2 (con Xcode 7), Arrayincluye un indexOfmétodo proporcionado por el CollectionTypeprotocolo. (En realidad, dos indexOfmétodos: uno que usa la igualdad para un argumento y otro que usa un cierre).

Antes de Swift 2, no había una forma para que los tipos genéricos, como las colecciones, proporcionaran métodos para los tipos concretos derivados de ellos (como las matrices). Entonces, en Swift 1.x, "índice de" es una función global ... Y también se renombró, así que en Swift 1.x, esa función global se llama find.

También es posible (pero no necesario) usar el indexOfObjectmétodo de NSArray... o cualquiera de los otros métodos de búsqueda más sofisticados de Foundation que no tienen equivalentes en la biblioteca estándar de Swift. Solo import Foundation(u otro módulo que importe de manera transitiva Foundation), emite tuArray a NSArray, y puede usar los muchos métodos de búsqueda NSArray.

rickster
fuente
4

Cualquiera de esta solución me funciona

Esta es la solución que tengo para 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
fuente
2

Si todavía está trabajando en Swift 1.x

entonces intenta,

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

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

Para SWIFT 3 puede usar una función simple

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

Esto le dará la posición del número, por lo que puede usar como

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

Espero ser útil

Marco
fuente
1

SWIFT 4

Digamos que desea almacenar un número de la matriz llamada cardButtons en cardNumber, puede hacerlo de esta manera:

let cardNumber = cardButtons.index(of: sender)

remitente es el nombre de su botón


fuente
0

Swift 4

Para tipos de referencia:

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
fuente
0

En Swift 4/5, use "firstIndex" para encontrar el índice.

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

En caso de que alguien tenga este problema

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

solo haz esto

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

entonces

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

por (>= swift 4.0)

Es bastante muy simple. Considere el siguiente Arrayobjeto.

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

Para obtener el índice del elemento rose, todo lo que tiene que hacer es:

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

Nota:

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

  • nil implica que el elemento no está presente en la matriz.

  • Es posible que desee desenrollar forzosamente el valor devuelto o usar un if-letpara evitar lo opcional.

Mohammed Sadiq
fuente
0

Simplemente use el método firstIndex.

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