Logan
Logan

Reputation: 53112

Comparing objects in an Array extension causing error in Swift

I'm trying to build an extension that adds some of the convenience functionality of NSArray/NSMutableArray to the Swift Array class, and I'm trying to add this function:

func indexOfObject(object:AnyObject) -> Int? {

    if self.count > 0 {
        for (idx, objectToCompare) in enumerate(self) {
            if object == objectToCompare {
                return idx
            }
        }
    }

    return nil
}

But unfortunately, this line:

if object == objectToCompare {

Is giving the error:

could not find an overload for '==' that accepts the supplied arguments

Question

What am I doing wrong to cause this error?

Example

extension Array {

    func indexOfObject(object:AnyObject) -> Int? {

        if self.count > 0 {
            for (idx, objectToCompare) in enumerate(self) {
                if object == objectToCompare {
                    return idx
                }
            }
        }

        return nil
    }

} 

Upvotes: 2

Views: 2906

Answers (6)

matt
matt

Reputation: 535304

Actually there is no need to implement indexOfObject:; there is a global function find(array, element) already.

Upvotes: 7

Jean Le Moignan
Jean Le Moignan

Reputation: 22236

You were close. Here's a working extension:

extension Array {
    func indexOfObject<T: Equatable>(object:T) -> Int? {
        if self.count > 0 {
            for (idx, objectToCompare) in enumerate(self) {
                if object == objectToCompare as T {
                    return idx
                }
            }
        }
        return nil
    }
}

Swift had no way of knowing if object or objectToCompare were equatable. By adding generic information to the method, we're then in business.

Upvotes: 0

shucao
shucao

Reputation: 2242

You can extract the compare part to another helper function, for example

extension Array {
    func indexOfObject(object: T, equal: (T, T) -> Bool) -> Int? {

        if self.count > 0 {
            for (idx, objectToCompare) in enumerate(self) {
                if equal(object, objectToCompare) {
                    return idx
                }
            }
        }

        return nil
    }
}

let arr = [1, 2, 3]

arr.indexOfObject(3, ==) // which returns {Some 2}

Upvotes: 0

Cezary Wojcik
Cezary Wojcik

Reputation: 21845

Here's a relevant example from Apple's "The Swift Programming Language" in the "Generics" section:

func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
    for (index, value) in enumerate(array) {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

The key idea here is that both value and valueToFind must of a type that is guaranteed to have the == operator implemented/overloaded. The <T: Equatable> is a generic that allows only objects of a type that are, well, equatable.

In your case, we would need to ensure that the array itself is composed only of objects that are equatable. The Array is declared as a struct with a generic <T> that does not require it to be equatable, however. I don't know whether it is possible to use extensions to change what kind of types an array can be composed of. I've tried some variations on the syntax and haven't found a way.

Upvotes: 0

mythz
mythz

Reputation: 143339

You can always create an extension that uses NSArray's indexOfObject, e.g:

extension Array {
    func indexOfObject(object:AnyObject) -> Int? {
        return (self as NSArray).indexOfObject(object)
    }
}

You can specify that your array items can be compared with the <T : Equatable> constraint, then you can cast your object into T and compare them, e.g:

extension Array {
    func indexOfObject<T : Equatable>(o:T) -> Int? {
        if self.count > 0 {
            for (idx, objectToCompare) in enumerate(self) {
                let to = objectToCompare as T
                if o == to {
                    return idx
                }
            }
        }

        return nil
    }
}

Upvotes: 6

Gregory Higley
Gregory Higley

Reputation: 16558

My guess is that you have to do something like this:

func indexOfObject<T: Equatable>(object: T) -> Int? {

and so on.

Upvotes: 2

Related Questions