Como encontrar o índice do item de lista no Swift?

442

Estou tentando encontrar um item indexpesquisando um list. alguém sabe como fazer aquilo?

Eu vejo que existe list.StartIndexe list.EndIndexquero algo como o de python list.index("text").

Chéyo
fonte

Respostas:

822

Como o swift é, em alguns aspectos, mais funcional do que o orientado a objetos (e Arrays são estruturas, não objetos), use a função "find" para operar na matriz, que retorna um valor opcional, portanto, esteja preparado para lidar com um valor nulo:

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

Atualização para o Swift 2.0:

A findfunção antiga não é mais suportada no Swift 2.0!

Com o Swift 2.0, Arrayobtém a capacidade de encontrar o índice de um elemento usando uma função definida em uma extensão 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

Além disso, a localização do primeiro elemento em uma matriz que cumpre um predicado é suportada por outra extensão 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

Observe que essas duas funções retornam valores opcionais, como findantes.

Atualização para o Swift 3.0:

Observe que a sintaxe de indexOf foi alterada. Para itens em conformidade com Equatablevocê, você pode usar:

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

Uma documentação detalhada do método pode ser encontrada em https://developer.apple.com/reference/swift/array/1689674-index

Para itens de matriz que não estão em conformidade, Equatablevocê precisará usar index(where:):

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

Atualização para o Swift 4.2:

Com o Swift 4.2, indexnão é mais usado, mas é separado firstIndexe lastIndexpara melhor esclarecimento. Portanto, dependendo de você estar procurando o primeiro ou o último índice do item:

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

let indexOfA = arr.firstIndex(of: "a") // 0
let indexOfB = arr.lastIndex(of: "a") // 3
Sebastian Schuth
fonte
22
Onde você aprendeu sobre a função find? Eu não consigo encontrar qualquer documentação de "encontrar" ou quaisquer outras funções globais
Johannes
14
Vindo do mundo OOP, como devo encontrar esse tipo de funções de flutuação livre?
Rudolf Adamkovič
4
Eles parecem não ter documentos. Consulte práticoswift.com/ 2014/ 06/14/… para obter uma lista (mas lembre-se de que eu não verifiquei se a lista está completa ou atualizada).
Sebastian Schuth
5
Johannes e @Rudolf - use Dash.app. É um aplicativo OS X para navegar na documentação. Ele possui uma referência de idioma para o Swift que possui uma lista de todas as funções de flutuação livre. Fácil de filtrar e pesquisar. Não posso viver sem ele.
Bjorn
4
FYI: Se você estiver usando indexOfum conjunto de estruturas que você mesmo definiu, sua estrutura deve estar em conformidade com o Equatableprotocolo.
Matthew Quiros
202

tl; dr:

Para as aulas, você pode estar procurando:

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

Resposta completa:

Eu acho que vale a pena mencionar que, com os tipos de referência ( class), você pode querer fazer uma comparação de identidade ; nesse caso, você só precisa usar o ===operador de identidade no fechamento do 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

Observe que a sintaxe acima usa a sintaxe de fechamento à direita e é equivalente a:

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


Swift 4 / Swift 3 - a função costumava ser chamada index

Swift 2 - a função costumava ser chamada indexOf

* Observe o comentário relevante e útil de paulbailey sobre os classtipos implementados Equatable, nos quais é necessário considerar se você deve comparar usando ===( operador de identidade ) ou ==( operador de igualdade ). Se você decidir combinar o uso ==, poderá simplesmente usar o método sugerido por outras pessoas ( people.firstIndex(of: person1)).

Nikolay Suvandzhiev
fonte
6
Esta é uma publicação útil para quem se pergunta por que .indexOf (x) não funciona - esta solução é inesperada, mas perfeitamente óbvia em retrospecto.
Maury Markowitz
1
Muito obrigado por isso, mas não é óbvio para mim. Examinei a documentação e realmente não entendo por que precisaria de um fechamento de predicado ao usar indexOf em um tipo de referência? Parece que o indexOf já deve poder lidar com tipos de referência por conta própria. Deve saber que é um tipo de referência e não um tipo de valor.
22416 Chris
7
Se Personimplementado o Equatableprotocolo, isso não seria necessário.
21916 Paulbailey
2
Estou recebendo: Binary operator '===' cannot be applied to operands of type '_' and 'Post', Posté o meu struct ... alguma idéia?
David Seek
@DavidSeek, structs(e enums) são tipos de valor, não tipos de referência. Somente tipos de referência (por exemplo class) possuem lógica de comparação de identidade ( ===). Confira as outras respostas sobre o que fazer structs(basicamente você simplesmente usa o array.index(of: myStruct), certificando-se de que o tipo myStructestá em conformidade com Equatable( ==)).
Nikolay Suvandzhiev
81

Você pode filteruma matriz com um fechamento:

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

E você pode contar uma matriz:

filtered.count // <= returns 1

Portanto, você pode determinar se uma matriz inclui seu elemento combinando estes:

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

Se você deseja encontrar a posição, não vejo uma maneira elegante, mas certamente você pode fazê-lo assim:

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

Enquanto estamos nisso, para um exercício divertido, vamos estender Arraypara ter um findmétodo:

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

Agora podemos fazer isso:

myList.find { $0 == 3 }
// returns the index position of 3 or nil if not found
gwcoffey
fonte
Para os divertidos, adicionei outro exemplo em que estendo o builtin Arraypara ter um findmétodo que faça o que você deseja. Ainda não sei se essa é uma boa prática, mas é uma experiência interessante.
precisa saber é o seguinte
2
Só quero ressaltar que você deve usar ++ idx conforme os documentos: "A menos que você precise do comportamento específico do i ++, é recomendável usar ++ iei - em todos os casos, porque eles têm o esperado típico comportamento de modificar ie retornar o resultado ".
Logan
Boa decisão. Enquanto você publicava isso, eu o revisei para usá- enumeratelo, para que não se aplique mais, mas você está absolutamente certo.
precisa saber é o seguinte
Sim, lembrei-me de notá-lo em um dos exemplos quando estava passando. Tentando definir-me o hábito agora :)
Logan
4
Para que isso funcione no Swift 2 / XCode 7, é necessário modificá-lo da seguinte maneira. Substitua (includedElement: T -> Bool) por (includedElement: Element -> Bool) e altere enumerate (self) para self.enumerate
Scooter
35

Swift 5

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

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

Exemplo 1

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

Exemplo2

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

Enquanto indexOf()funciona perfeitamente, ele retorna apenas um índice.

Eu estava procurando uma maneira elegante de obter uma matriz de índices para elementos que satisfazem alguma condição.

Aqui está como isso pode ser feito:

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

Para classe personalizada, você precisa implementar o 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)
ZYiOS
fonte
9

Atualização para o Swift 2:

sequence.contains (elemento) : retorna true se uma determinada sequência (como uma matriz) contiver o elemento especificado.

Swift 1:

Se você está olhando apenas para verificar se um elemento está contido em uma matriz, ou seja, obtenha um indicador booleano, use em contains(sequence, element)vez de find(array, element):

contém (sequência, elemento) : retorna true se uma determinada sequência (como uma matriz) contiver o elemento especificado.

Veja o exemplo abaixo:

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

no Swift 4.2

.index (where :) foi alterado para .firstIndex (where :)

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

Swift 4. Se sua matriz contiver elementos do tipo [String: AnyObject]. Então, para encontrar o índice do elemento, use o código abaixo

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 Se você deseja encontrar o índice com base na chave do Dicionário. Aqui a matriz contém objetos da classe Model e eu estou correspondendo a propriedade 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
fonte
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
fonte
3
você está mudando um let array? O uso de selfé questionável também.
Chéyo 10/11/2015
4

No Swift 4 , se você estiver percorrendo sua matriz DataModel, verifique se o modelo de dados está em conformidade com o Equatable Protocol, implemente o método lhs = rhs e somente então você poderá usar ".index (of". Por exemplo

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

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

E depois,

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

No Swift 2 (com o Xcode 7), Arrayinclui um indexOfmétodo fornecido pelo CollectionTypeprotocolo. (Na verdade, dois indexOfmétodos - um que usa igualdade para combinar com um argumento e outro que usa um fechamento.)

Antes do Swift 2, não havia uma maneira de tipos genéricos, como coleções, fornecerem métodos para os tipos concretos derivados deles (como matrizes). Então, no Swift 1.x, "índice de" é uma função global ... E também foi renomeada, portanto, no Swift 1.x, essa função global é chamada find.

Também é possível (mas não necessário) usar o indexOfObjectmétodo de NSArray... ou qualquer outro método de pesquisa mais sofisticado da Foundation que não tenha equivalentes na biblioteca padrão do Swift. Apenas import Foundation(ou outro módulo que importa transitivamente o Foundation), faça a conversão Arraypara NSArraye você poderá usar os vários métodos de pesquisa NSArray.

rickster
fonte
4

Qualquer uma dessas soluções funciona para mim

Esta é a solução que tenho para o 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
fonte
2

Você também pode usar a biblioteca funcional Dollar para executar um indexOf em uma matriz como tal http://www.dollarswift.org/#indexof-indexof

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

Se você ainda estiver trabalhando no Swift 1.x

Então tente,

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

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

Para o SWIFT 3, você pode usar uma função simples

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

Isso fornecerá a posição numérica, para que você possa usar como

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

Espero ser útil

Marco
fonte
1

SWIFT 4

Digamos que você queira armazenar um número da matriz chamada cardButtons em cardNumber, você pode fazer o seguinte:

let cardNumber = cardButtons.index(of: sender)

remetente é o nome do seu botão


fonte
0

Swift 4

Para tipos de referência:

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

No Swift 4/5, use "firstIndex" para encontrar o índice.

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

Caso alguém tenha esse problema

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

apenas faça isso

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

então

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

Para (>= swift 4.0)

É bastante simples. Considere o seguinte Arrayobjeto.

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

Para obter o índice do elemento rose, tudo o que você precisa fazer é:

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

Nota:

  • Array.index(of:)retorna um Optional<Int>.

  • nil implica que o elemento não está presente na matriz.

  • Você pode querer desembrulhar o valor retornado ou usar um if-letpara contornar o opcional.

Mohammed Sadiq
fonte
0

Basta usar o método firstIndex.

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