Rivera Rivera - 1 month ago 10
Swift Question

Swift Cannot invoke 'find' with an argument list of type '([Score], Score)' where Score is a struct

While

find(["a", "b"], "c")
works with no problems, I get an error when trying to find the index of a structure inside an array of structures:

struct Score
{
//...
}

var scores: [Score] = //...
var score: Score = //...

find(self.scores, score) // Error: Cannot invoke 'find' with an argument list of type '([Score], Score)'


I though it could be a problem with structures that can't be compared to each other by default. But changing
Score
s definition to
class

gives me the same error.

Answer

EDIT: as of Swift 2.0, there is now a built-in version of find that takes a closure so you don’t have to write your own – but also, find has been renamed indexOf, and is now a protocol extension of CollectionType, so you call it like a method:

// if you make `Score` conform to `Equatable:
if let idx = self.scores.indexOf(score) {

}

// or if you don't make it Equatable, you can just use a closure:
// (see original answer below for why you might prefer to do this)
if let idx = scores.indexOf({$0.scoreval == 3}) {

}

Original pre-2.0 answer below


While the answers suggesting making your class Equatable may work nicely, I'd recommend a bit of caution before choosing to do this. The reason being that, as the docs state, equatability implies substitutability, and your == operator must be reflexive, symmetric and transitive. If you don't adhere to this, you might get some very weird behavior when using algorithms like equals, sort etc. Be especially cautious if implementing Equatable on non-final classes. If you're sure you can meet requirements, go for it, and find will work.

If not, an alternative you could consider is writing a function that ought to be in the standard library but isn't, which is a find that takes a closure:

func find<C: CollectionType>(source: C, match: C.Generator.Element -> Bool) -> C.Index {
    for idx in indices(source) {
        if match(source[idx]) { return idx }
    }
    return nil
}

Once you have this, you can supply any matching criteria you prefer. For example, if your objects are classes you could use reference equality:

let idx = find(scores) { $0 === $1 }
Comments