简体   繁体   中英

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
    }

} 

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

You can always create an extension that uses NSArray's indexOfObject, eg:

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, eg:

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

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

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

and so on.

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.

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}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM