Question or problem with Swift language programming:
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”).
How to solve the problem:
Solution 1:
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
Update for Swift 2.0:
The old find
function is not supported any more with Swift 2.0!
With Swift 2.0, Array
gains the ability to find the index of an element using a function defined in an extension of CollectionType
(which Array
implements):
let arr = ["a","b","c"] let indexOfA = arr.indexOf("a") // 0 let indexOfB = arr.indexOf("b") // 1 let indexOfD = arr.indexOf("d") // nil
Additionally, finding the first element in an array fulfilling a predicate is supported by another extension of 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
Note that these two functions return optional values, as find
did before.
Update for Swift 3.0:
Note the syntax of indexOf has changed. For items conforming to Equatable
you can use:
let indexOfA = arr.index(of: "a")
A detailed documentation of the method can be found at https://developer.apple.com/reference/swift/array/1689674-index
For array items that don’t conform to Equatable
you’ll need to use index(where:)
:
let index = cells.index(where: { (item) -> Bool in item.foo == 42 // test if this is the item you're looking for })
Update for Swift 4.2:
With Swift 4.2, index
is no longer used but is separated into firstIndex
and lastIndex
for better clarification. So 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
Solution 2:
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)
).
Solution 3:
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
Solution 4:
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"]"
Solution 5:
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)