2

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
    }

} 
6
  • Are you trying to compare the objects themselves or just check if they point to the same object? Commented Jun 4, 2014 at 6:12
  • Is this an exercise? You should be able to use Cocoa/NSArray's indexOfObject: on an Array. Commented Jun 4, 2014 at 6:13
  • @JoshCaswell -- Yes, how do they do it? Commented Jun 4, 2014 at 6:14
  • How do they do which? Commented Jun 4, 2014 at 6:14
  • @JoshCaswell -- The indexOfObject isn't available on Array type variables, only on NSArray type variables. I'd like to add an extension that provides the functionality w/ native swift w/o the need to bridge to objective-c -- mostly just because I'm experimenting. Commented Jun 4, 2014 at 6:19

6 Answers 6

7

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

Sign up to request clarification or add additional context in comments.

2 Comments

It came as a surprise to me too.
Any chance you could point me to where you found this? Possibly a place where others like it exist?
6

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

3 Comments

Is there much of a cost involved in the bridging? I guess my thinking was that keeping it native would ultimately be better in the long run.
@Logan I imagine calling Obj-C code from Swift would be highly optimized
Any thoughts on how to compare objects like this in general? I'd perhaps like to implement similar functionality in an extension.
2

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

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

and so on.

4 Comments

I'm sorry if this is painfully obvious, but would you mind elaborating just a touch more. I'm getting the same error with this.
We can't know that there exists a == operator for any two AnyObjects. The <> syntax specifies a generic. They're like templates in C++. The Equatable keyword basically means that a comparison operator for T == T exists.
I, too, get the same error, but the docs say that Equatable objects must define the == operator, yet it still doesn't seem to compile. I suspect it's because Swift is not yet ready for prime time.
Actually, if I'd spent two seconds reading mythz's answer, I would have seen why.
0

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.

Comments

0

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}

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.