ChatGPT解决这个技术问题 Extra ChatGPT

How to find index of list item in Swift?

I am trying to find an item index by searching a list. Does anybody know how to do that?

I see there is list.StartIndex and list.EndIndex but I want something like python's list.index("text").


j
johndpope

As swift is in some regards more functional than object-oriented (and Arrays are structs, not objects), use the function "find" to operate on the array, which returns an optional value, so be prepared to handle a nil value:

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

Use firstIndex and lastIndex - depending on whether you are looking for the first or last index of the item:

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

let indexOfA = arr.firstIndex(of: "a") // 0
let indexOfB = arr.lastIndex(of: "a") // 3

Where did you learn about the find function? I can't seem to find any documentation of "find" or any other global functions
Coming from the OOP world, how am I supposed to find this kind of free floating functions?
They seem to be largely undocumented. See practicalswift.com/2014/06/14/… for a list (but be aware that i did not check if the list is complete or up to date).
Johannes and @Rudolf - use Dash.app. It's an OS X app for browsing documentation. It has a language reference for Swift that has a list of all the free floating functions. Easy to filter and search. Can't live without it.
FYI: If you're using indexOf on an array of structs you defined yourself, your struct has to comply with the Equatable protocol.
N
Nikolay Suvandzhiev

tl;dr:

For classes, you might be looking for:

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

Full answer:

I think it's worth mentioning that with reference types (class) you might want to perform an identity comparison, in which case you just need to use the === identity operator in the predicate closure:

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

Note that the above syntax uses trailing closures syntax, and is equivalent to:

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


Swift 4 / Swift 3 - the function used to be called index

Swift 2 - the function used to be called indexOf

* Note the relevant and useful comment by paulbailey about class types that implement Equatable, where you need to consider whether you should be comparing using === (identity operator) or == (equality operator). If you decide to match using ==, then you can simply use the method suggested by others (people.firstIndex(of: person1)).


This is a useful post for those wondering why .indexOf(x) doesn't work - this solution is unexpected but perfectly obvious in retrospect.
Thanks so much for this but it's not obvious to me at all. I looked at the documentation and I really don't understand why would I need a predicate closure when using indexOf on a reference type? It feels like indexOf should already be able to handle reference types on its own. It should know that it's a reference type and not a value type.
If Person implemented the Equatable protocol, this wouldn't be needed.
i'm receiving: Binary operator '===' cannot be applied to operands of type '_' and 'Post' , Post is my struct... any idea?
@DavidSeek, structs (and enums) are value types, not reference types. Only reference types (e.g class) have identity comparison logic (===). Check out the other answers for what to do with structs (basically you simply use the array.index(of: myStruct), making sure type of myStruct conforms to Equatable (==)).
g
gwcoffey

You can filter an array with a closure:

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

And you can count an array:

filtered.count // <= returns 1

So you can determine if an array includes your element by combining these:

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

If you want to find the position, I don't see fancy way, but you can certainly do it like this:

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

EDIT

While we're at it, for a fun exercise let's extend Array to have a find method:

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

Now we can do this:

myList.find { $0 == 3 }
// returns the index position of 3 or nil if not found

For funsies I added another example where I extend the builtin Array to have a find method that does what you want. I don't know yet if this is a good practice, but it is a neat experiment.
Just want to point out, that you should use ++idx as per docs: "Unless you need the specific behavior of i++, it is recommended that you use ++i and --i in all cases, because they have the typical expected behavior of modifying i and returning the result."
Good call. As you were posting this I was revising it to use enumerate so it no longer applies, but you're absolutely right.
Yea, I remembered noticing it in one of the examples when I was going through. Trying to set myself in the habit now :)
For this to work under Swift 2 / XCode 7 you need to modify it as follows. Replace (includedElement: T -> Bool) with (includedElement: Element -> Bool) and change enumerate(self) to self.enumerate
T
TheAlienMann

Swift 5

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

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

Example1

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

Example2

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

The where version has this signature: func firstIndex(where: (Element) -> Bool) -> Int? And, since firstIndex has a trailing closure you could write for Example 1: let index = alphabets.firstIndex { $0 == "A" } :-)
S
Serhii Yakovenko

While indexOf() works perfectly, it only returns one index.

I was looking for an elegant way to get an array of indexes for elements which satisfy some condition.

Here is how it can be done:

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)

Be careful when using this on subsequences, offset it is not the same as an index. Not all collections indices starts from zero. Note that this would only work with Equatable elements. let indices = array.indices.filter { array[$0].contains("og") } or using zip let indices = zip(array.indices, array).filter { $1.contains("og") }.map(\.0)
R
Ridho Octanio

in Swift 4.2

.index(where:) was changed to .firstIndex(where:)

array.firstIndex(where: {$0 == "person1"})

Z
ZYiOS

For custom class, you need to implement the Equatable protocol.

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)

e
erdikanik

Just use firstIndex method.

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

Error: Binary operator '==' cannot be applied to two 'Element' operands
D
Dwigt

In Swift 4, the firstIndex method can be used. An example of using the == equality operator to find an object in an array by its id:

let index = array.firstIndex{ $0.id == object.id }

note this solution avoids your code needing to conform to the Equitable protocol as we're comparing the property and not the entire object

Also, a note about == vs === since many of the answers posted so far have differed in their usage:

== is the equality operator. It checks if values are equal.

=== is the identity operator. It checks whether two instances of a class point to the same memory. This is different from equality, because two objects that were created independently using the same values will be considered equal using == but not === because they are different objects. (Source)

It would be worth it to read more on these operators from Swift's documentation.


B
Bretsko

Update for Swift 2:

sequence.contains(element): Returns true if a given sequence (such as an array) contains the specified element.

Swift 1:

If you're looking just to check if an element is contained inside an array, that is, just get a boolean indicator, use contains(sequence, element) instead of find(array, element):

contains(sequence, element): Returns true if a given sequence (such as an array) contains the specified element.

See example below:

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

G
Gurjinder Singh

Swift 4. If your array contains elements of type [String: AnyObject]. So to find the index of element use the below code

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)

Or If you want to found index on the basis of key from Dictionary. Here array contains Objects of Model class and I am matching id property.

   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

N
Naishta

In Swift 4, if you are traversing through your DataModel array, make sure your data model conforms to Equatable Protocol , implement the lhs=rhs method , and only then you can use ".index(of" . For example

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

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

And then,

let index = self.photos.index(of: aPhoto)

m
mzaink

For (>= swift 4.0)

It's rather very simple. Consider the following Array object.

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

In order to obtain the index of the element rose, all you have to do is:

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

Note:

Array.index(of:) returns an Optional.

nil implies that the element isn't present in the array.

You might want to force-unwrap the returned value or use an if-let to get around the optional.


L
Luca Davanzo

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)

you are mutating a let array? The use of self is questionable also.
C
Cœur

In Swift 2 (with Xcode 7), Array includes an indexOf method provided by the CollectionType protocol. (Actually, two indexOf methods—one that uses equality to match an argument, and another that uses a closure.)

Prior to Swift 2, there wasn't a way for generic types like collections to provide methods for the concrete types derived from them (like arrays). So, in Swift 1.x, "index of" is a global function... And it got renamed, too, so in Swift 1.x, that global function is called find.

It's also possible (but not necessary) to use the indexOfObject method from NSArray... or any of the other, more sophisticated search meth dis from Foundation that don't have equivalents in the Swift standard library. Just import Foundation (or another module that transitively imports Foundation), cast your Array to NSArray, and you can use the many search methods on NSArray.


K
Kevin ABRIOUX

Any of this solution works for me

This the solution i have for 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
        })

E
Encore PTL

You can also use the functional library Dollar to do an indexOf on an array as such http://www.dollarswift.org/#indexof-indexof

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

i
iCyberPaul

If you are still working in Swift 1.x

then try,

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

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

M
Marco

For SWIFT 3 you can use a simple function

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

This will give the number position, so you can use like

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

Hope to be useful


K
Krunal Patel

In Swift 4/5, use "firstIndex" for find index.

let index = array.firstIndex{$0 == value}

M
Menno

Swift 4

For reference types:

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

A
Ahmed Safadi

In case somebody has this problem

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

jsut do this

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

then

guard let finalIndex = index?.toInt else {
    return false
}

佚名

SWIFT 4

Let's say you want to store a number from the array called cardButtons into cardNumber, you can do it this way:

let cardNumber = cardButtons.index(of: sender)

sender is the name of your button