Swift: Trying to do an any-type Array lookup

So I’m in the middle of trying to create a function that allows you to look up any element on an array. And this is where I’ve more or less hit a brick wall. This is the starting point function. Update: scroll down for more

extension Array {
    func indexOfElement<T:Equatable>(sought:T) -> Int? {
        for i in 0..self.count
        {
            if ((self[i] as? T) == sought)
            {
                return i
            }
        }
        return nil
    }
}

It works pretty well on all the following examples, letting me search through arrays regardless of parameter type.

let abc = ["a", "b", "c"]
abc.indexOfElement(77)
abc.indexOfElement("c")

let a01 = [0, 1, 5, 3, 9]
a01.indexOfElement(5)
a01.indexOfElement(99)
a01.indexOfElement("hello")

Here’s where it gets ugly. When I create an array with mixed elements, it completely fails. All of these test cases return nil.

// Miserable failure
let a02 : Array = [0, 1, "HELLO", 3, 9]
a02.indexOfElement("Cow")
a02.indexOfElement(9)
a02.indexOfElement("HELLO")

If you have any insight on what’s going wrong, I’d love to hear about it. The best I can figure at this point is that the a02 array is forced to be an NSArray rather than an Array, but I’m not sure of the details.

UPDATE: So after a bit of slogging, I confirmed that the mixed elements default to an NSArray. Adding an NSArray extension fixed the problem as you see below. Thanks everyone who commented, responded to me on Twitter, and in IRC.

import Foundation

let array1 = [1, 2, 3, 4, 5]
let array2 = ["a", "b", "c", "d"]
let array3 = [1, 2, "a"]
let array4 = array3 as AnyObject[] // don't try to force this

// Oh this is interesting. They look different
println(array1)
println(array2)
println(array3)

// 3 appears to be an NSArray of some kind
println(array3 as NSArray)
println(array2 as NSArray)

// In addition to
extension Array {
    func indexOfElement(item:T) -> Int? {
        for i in 0..self.count
        {
            if ((self[i] as? T) == item)
            {
                return i
            }
        }
        return nil
    }
}

// Just add this
extension NSArray {
    func indexOfElement(item : AnyObject?) -> Int?
    {
        return self.indexOfObject(item)
    }
}

// And now these all work
array2.indexOfElement("c")
array2.indexOfElement(2)
array2.indexOfElement("d")
array3.indexOfElement(2)
array3.indexOfElement(1)
array3.indexOfElement("a")

// Just avoid doing stuff with array 4

5 Comments

  • I think Apple had mentioned something about putting different types of objects in an array in Swift. They said if you were going to do that you might as well just use a dictionary because arrays are only supposed to be used for one type of object.

  • What if you made the type of a02 AnyObject?

  • From the Swift Book:

    “Arrays store ordered lists of values of the same type. Dictionaries store unordered collections of values of the same type”

    So i guess that collections from different types are not an ok thing to do?

  • Updating now, but yes, the problem is that it (currently) automatically creates an NSArray from mixed items

  • Probably you know this, but there is also the built in function “find”, which will work for anything conforming to the Collection protocol, which the Swift Arrays does. It returns an optional value with the index of the element.

    let v = [1,2,3,100,5]
    find(v,100)

    The signature of find is at line 4338 in the Swift kit source listing.

    But this won’t handle heterogenous arrays, because Swift won’t, I think.