Jak znaleźć indeks elementu listy w Swift?

443

Próbuję znaleźć item index, wyszukująclist . Czy ktoś wie jak to zrobić?

Widzę tam jest list.StartIndex, a list.EndIndexjednak chcę coś takiego Pythona list.index("text").

Czéyo
źródło

Odpowiedzi:

823

Ponieważ szybkie jest pod pewnymi względami bardziej funkcjonalne niż obiektowe (a tablice są strukturami, a nie obiektami), użyj funkcji „znajdź”, aby operować na tablicy, która zwraca wartość opcjonalną, więc przygotuj się na obsługę wartości zerowej:

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

Aktualizacja dla Swift 2.0:

Stara findfunkcja nie jest już obsługiwana w Swift 2.0!

Dzięki Swift 2.0 Arrayzyskuje możliwość znalezienia indeksu elementu za pomocą funkcji zdefiniowanej w rozszerzeniu CollectionType(który Arrayimplementuje):

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

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

Ponadto znalezienie pierwszego elementu w tablicy spełniającej predykat jest obsługiwane przez inne rozszerzenie 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

Zauważ, że te dwie funkcje zwracają wartości opcjonalne, tak findjak wcześniej.

Aktualizacja dla Swift 3.0:

Uwaga: zmieniono składnię indexOf. Do przedmiotów zgodnych Equatablemożesz użyć:

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

Szczegółowa dokumentacja metody znajduje się na stronie https://developer.apple.com/reference/swift/array/1689674-index

W przypadku elementów tablicy, które nie są zgodne Equatable, musisz użyć index(where:):

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

Aktualizacja dla Swift 4.2:

W Swift 4.2 indexnie jest już używany, ale jest podzielony na firstIndexi lastIndexdla lepszego wyjaśnienia. W zależności od tego, czy szukasz pierwszego czy ostatniego indeksu przedmiotu:

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

let indexOfA = arr.firstIndex(of: "a") // 0
let indexOfB = arr.lastIndex(of: "a") // 3
Sebastian Schuth
źródło
22
Gdzie dowiedziałeś się o funkcji wyszukiwania? Nie mogę znaleźć żadnej dokumentacji „find” ani innych globalnych funkcji
Johannes,
14
Pochodząc ze świata OOP, jak mam znaleźć tego rodzaju swobodne funkcje pływające?
Rudolf Adamkovič,
4
Wydają się być w dużej mierze nieudokumentowane. WidziećListę można praktyczneswift.com/2014/06/14/ ... (należy jednak pamiętać, że nie sprawdziłem, czy lista jest pełna lub aktualna).
Sebastian Schuth,
5
Johannes i @Rudolf - użyj Dash.app. Jest to aplikacja OS X do przeglądania dokumentacji. Ma odniesienie językowe do Swift, które zawiera listę wszystkich swobodnych funkcji swobodnych. Łatwe filtrowanie i wyszukiwanie. Nie da się bez tego żyć.
Bjorn,
4
FYI: Jeśli korzystasz indexOfz tablicy struktur, które sam zdefiniowałeś, Twoja struktura musi być zgodna z Equatableprotokołem.
Matthew Quiros
202

tl; dr:

Na zajęcia możesz szukać:

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

Pełna odpowiedź:

Myślę, że warto wspomnieć, że w przypadku typów referencyjnych ( class) można wykonać porównanie tożsamości , w takim przypadku wystarczy użyć ===operatora tożsamości w zamknięciu predykatu:


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

Zauważ, że powyższa składnia używa końcowej składni zamknięć i jest równoważna z:

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


Swift 4 / Swift 3 - wywoływana funkcja index

Swift 2 - wywoływana funkcja indexOf

* Zwróć uwagę na odpowiedni i użyteczny komentarz autorstwa paulbailey na temat classtypów, które się implementują Equatable, gdzie musisz rozważyć, czy powinieneś porównywać używając ===( operator tożsamości ) czy ==( operator równości ). Jeśli zdecydujesz się dopasować za pomocą ==, możesz po prostu użyć metody sugerowanej przez innych ( people.firstIndex(of: person1)).

Nikolay Suvandzhiev
źródło
6
Jest to przydatny post dla tych, którzy zastanawiają się, dlaczego .indexOf (x) nie działa - to rozwiązanie jest nieoczekiwane, ale całkowicie oczywiste z perspektywy czasu.
Maury Markowitz,
1
Dziękuję bardzo za to, ale wcale nie jest to dla mnie oczywiste. Przejrzałem dokumentację i naprawdę nie rozumiem, dlaczego miałbym potrzebować zamknięcia predykatu, gdy korzystam z indexOf dla typu referencyjnego? Wydaje się, że indexOf powinien już być w stanie samodzielnie obsługiwać typy referencyjne. Powinien wiedzieć, że jest to typ odniesienia, a nie typ wartości.
Chris
7
W przypadku Personzaimplementowania Equatableprotokołu nie byłoby to konieczne.
paulbailey
2
Otrzymuję: Binary operator '===' cannot be applied to operands of type '_' and 'Post', Postmoja struct ... jakiś pomysł?
David Seek
@DavidSeek, structs(i enums) są typami wartości, a nie typami referencyjnymi. Tylko typy referencyjne (np. class) Mają logikę porównywania tożsamości ( ===). Sprawdź inne odpowiedzi, aby dowiedzieć się, co z tym zrobić structs(w zasadzie po prostu używasz array.index(of: myStruct), upewniając się, że typ myStructjest zgodny z Equatable( ==)).
Nikolay Suvandzhiev,
81

Możesz filtertablicę z zamknięciem:

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

I możesz policzyć tablicę:

filtered.count // <= returns 1

Możesz więc ustalić, czy tablica zawiera Twój element, łącząc te:

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

Jeśli chcesz znaleźć pozycję, nie widzę wymyślnego sposobu, ale na pewno możesz to zrobić w następujący sposób:

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

EDYTOWAĆ

Skoro już to robimy, dla zabawy wykonajmy Arrayjeszcze jedną findmetodę:

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

Teraz możemy to zrobić:

myList.find { $0 == 3 }
// returns the index position of 3 or nil if not found
gwcoffey
źródło
Dla miłośników dodałem kolejny przykład, w którym rozszerzam wbudowane, Arrayaby mieć findmetodę, która robi to, co chcesz. Nie wiem jeszcze, czy to dobra praktyka, ale to fajny eksperyment.
gwcoffey
2
Chcę tylko zaznaczyć, że powinieneś używać ++ idx zgodnie z dokumentacją: „O ile nie potrzebujesz konkretnego zachowania i ++, zaleca się używanie ++ i i --i we wszystkich przypadkach, ponieważ mają one typowe oczekiwane zachowanie podczas modyfikowania i i zwracanie wyniku. ”
Logan
Dobra decyzja. Gdy to pisałeś, przeglądałem go, aby enumeratenie był już stosowany, ale masz całkowitą rację.
gwcoffey
Tak, przypomniałem sobie, że zauważyłem to w jednym z przykładów, kiedy przechodziłem. Próbuję się teraz przyzwyczaić :)
Logan
4
Aby działało pod Swift 2 / XCode 7, musisz go zmodyfikować w następujący sposób. Zamień (includedElement: T -> Bool) na (includedElement: Element -> Bool) i zmień wyliczenie (self) na self.enumerate
Scooter
35

Szybki 5

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

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

Przykład 1

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

Przykład 2

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

Chociaż indexOf()działa idealnie, zwraca tylko jeden indeks.

Szukałem eleganckiego sposobu na uzyskanie tablicy indeksów dla elementów, które spełniają pewne warunki.

Oto jak to zrobić:

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
źródło
12

W przypadku klasy niestandardowej należy zaimplementować protokół 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
źródło
9

Aktualizacja dla Swift 2:

sekwencji.contains (element) : Zwraca wartość true, jeśli dana sekwencja (na przykład tablica) zawiera określony element.

Swift 1:

Jeśli chcesz tylko sprawdzić, czy element jest zawarty w tablicy, to po prostu weź wskaźnik boolean, użyj contains(sequence, element)zamiast find(array, element):

zawiera (sekwencja, element) : Zwraca wartość true, jeśli dana sekwencja (na przykład tablica) zawiera określony element.

Zobacz przykład poniżej:

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
źródło
7

w Swift 4.2

.index (gdzie :) został zmieniony na .firstIndex (gdzie :)

array.firstIndex(where: {$0 == "person1"})
Ridho Octanio
źródło
6

Swift 4. Jeśli tablica zawiera elementy typu [String: AnyObject]. Aby znaleźć indeks elementu, użyj poniższego kodu

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)

Lub Jeśli chcesz znaleźć indeks na podstawie klucza ze słownika. Tutaj tablica zawiera obiekty klasy Model i dopasowuję właściwość 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
źródło
4

Swift 2.1

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

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

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

Szybki 3

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

if let index = array.index(of: "1") {
    array.remove(at: index)
}
array.remove(at: 1)
Luca Davanzo
źródło
3
mutujesz let array? Zastosowanie selfjest również wątpliwe.
Chéyo,
4

W Swift 4 , jeśli przemierzasz tablicę DataModel, upewnij się, że Twój model danych jest zgodny z protokołem Equatable, zaimplementuj metodę lhs = rhs, a dopiero potem możesz użyć „.index (z”. Na przykład

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

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

I wtedy,

let index = self.photos.index(of: aPhoto)
Naishta
źródło
4

W Swift 2 (z Xcode 7) Arrayzawiera indexOfmetodę dostarczoną przez CollectionTypeprotokół. (Właściwie dwie indexOfmetody - jedna używa równości w celu dopasowania argumentu, a druga używa zamknięcia.)

Przed wersją Swift 2 typy ogólne, takie jak kolekcje, nie zapewniały metod dla konkretnych typów pochodnych (takich jak tablice). Tak więc w Swift 1.x „indeks” jest funkcją globalną… I również zmieniono jego nazwę, więc w Swift 1.x ta funkcja globalna nazywa sięfind .

Możliwe jest również (ale nie konieczne) użycie indexOfObjectmetody z NSArray... lub dowolnej innej, bardziej wyrafinowanej metody wyszukiwania z Foundation, która nie ma odpowiedników w standardowej bibliotece Swift. Po prostu import Foundation(lub inny moduł, który tranzytowo importuje Foundation), prześlij swój ArraynaNSArray , i możesz korzystać z wielu metod wyszukiwania NSArray.

Rickster
źródło
4

Każde z tych rozwiązań działa dla mnie

To rozwiązanie mam dla 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
źródło
2

Możesz także użyć biblioteki funkcjonalnej Dollar do wykonania indeksu dla tablicy jako takiej http://www.dollarswift.org/#indexof-indexof

$.indexOf([1, 2, 3, 1, 2, 3], value: 2) 
=> 1
Bis PTL
źródło
2

Jeśli nadal pracujesz w Swift 1.x

więc spróbuj,

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

let indexOfA = find(testArray, "A") 
let indexOfB = find(testArray, "B")
let indexOfC = find(testArray, "C")
iCyberPaul
źródło
1

W SWIFT 3 możesz użyć prostej funkcji

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

To da numer pozycji, więc możesz użyć like

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

Mam nadzieję, że się przydadzę

Marco
źródło
1

SWIFT 4

Załóżmy, że chcesz zapisać liczbę z tablicy o nazwie cardButtons w cardNumber, możesz to zrobić w ten sposób:

let cardNumber = cardButtons.index(of: sender)

nadawca to nazwa twojego przycisku


źródło
0

Szybki 4

Dla typów referencyjnych:

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
źródło
0

W Swift 4/5 użyj „firstIndex” do znalezienia indeksu.

let index = array.firstIndex{$0 == value}
Krunal Patel
źródło
0

W przypadku, gdy ktoś ma ten problem

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

jsut zrób to

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

następnie

guard let finalIndex = index?.toInt else {
    return false
}
Ahmed Safadi
źródło
0

Dla (>= swift 4.0)

To raczej bardzo proste. Rozważ następujący Arrayobiekt.

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

Aby uzyskać indeks elementu rose, wystarczy:

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

Uwaga:

  • Array.index(of:)zwraca an Optional<Int>.

  • nil oznacza, że ​​element nie jest obecny w tablicy.

  • Możesz wymusić rozpakowanie zwróconej wartości lub użyć opcji, if-letaby obejść opcję opcjonalną.

Mohammed Sadiq
źródło
0

Wystarczy użyć metody firstIndex.

array.firstIndex(where: { $0 == searchedItem })
erdikanik
źródło