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.
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 }
The interface for the function find
is/was:
func find<C : CollectionType where C.Generator.Element : Equatable>(domain: C,
value: C.Generator.Element) -> C.Index?
This says that the CollectionType
of C
must have elements that are Equatable
and, furthermore, that the value
must also be Equatable
.
[Note Swift 3.0: As of Swift 3.0, you'll need to use the index
function which comes in two variations. In the first, you'll supply your own predicate:
func index(where: (Self.Generator.Element) -> Bool) -> Self.Index?
In the second, your elements need to be equatable:
// Where Generator.Element : Equatable
func index(of: Self.Generator.Element) -> Self.Index?
If you decide to go the equatable
route, then the following applies.
Note End]
Your Score
struct is not Equatable
and hence the error. You'll need to figure out what it means for scores to be equal to one another. Maybe it is some numerical 'score'; maybe it is a 'score' and a 'user id'. It depends on your Score
abstraction. Once you know, you implement ==
using:
func == (lhs:Score, rhs:Score) -> Bool {
return // condition for what it means to be equal
}
Note: if you use class
and thus scores have 'identity' then you can implement this as:
func == (lhs:Score, rhs:Score) -> Bool { return lhs === rhs }
Your example with strings works because String
is Equatable
. If you look in the Swift library code you'll see:
extension String : Equatable {}
func ==(lhs: String, rhs: String) -> Bool
As the others have said, the objects you search for must conform to the Equatable protocol.
So you need to add an extension to your Score struct that tells the compiler that it conforms to that protocol:
extension Score: Equatable {}
And then you need to implement the == function for that class:
public func ==(lhs: Score, rhs: Score) -> Bool
{
return lhs.whatever == rhs.whatever //replace with code for your struct.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With