问题
I'm trying to test if an array of type [Any]
contains a value of a specific type (say, Int
).
I'm aware that Swift does not know how to compare an Int
and a value of arbitrary type, and I guess that's what's being expressed in the autocomplete template:
contains(predicate: (protocol<>) throws -> Bool)
So I tried this code:
let intValue:Int = 5 // for example
let isContained = myArrayOfAny.contains({ element in
return ((element as? Int) == intValue)
})
...and it compiles (can't make sure it works yet); but still can't make heads or tails of the predicate: (protocol<>)
part. What does it mean? The documentation for SequenceType
is quite cryptic:
contains(_: (Self.Generator.Element) throws -> Bool) rethrows -> Bool
Default Implementation
Return true iff an element in self satisfies predicate.
Declaration
@warn_unused_result func contains(@noescape _ predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Bool
EDIT: I'm quite confused because, all search results I've seen have the contains()
method of Array
as just taking the value to be tested for containment, e.g.:
if myArrayOfInt.contains(someInt){
...
and not a closure.
回答1:
There are two different contains()
methods (both protocol
extensions to SequenceType
). The first one is
extension SequenceType where Generator.Element : Equatable {
/// Return `true` iff `element` is in `self`.
@warn_unused_result
public func contains(element: Self.Generator.Element) -> Bool
}
and it requires that the sequence elements conform to the Equatable
protocol, which guarantees that they can be compared with ==
:
public protocol Equatable {
// ...
public func ==(lhs: Self, rhs: Self) -> Bool
}
For example, in
let intValue:Int = 5
let myArrayOfInt = [4, 5, 6]
let isContained = myArrayOfInt.contains(intValue)
you have an array of Int
, and Int
conforms to Equatable
,
so this contains()
method can be used to check for the presence
of a certain element.
This does not work for
let myArrayOfAny : [Any] = [4, 5, 6]
because Any
does not conform to Equatable
. Here you can use
the second contains()
method
extension SequenceType {
/// Return `true` iff an element in `self` satisfies `predicate`.
@warn_unused_result
public func contains(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Bool
}
which takes a predicate, i.e. a boolean function. Instead of
comparing with ==
, the predicate is applied to each array element.
This method does not require that the array elements are Equatable
.
And that's what you do in
let myArrayOfAny : [Any] = [4, 5, 6]
let intValue:Int = 5 // for example
let isContained = myArrayOfAny.contains({ element in
return ((element as? Int) == intValue)
})
This will yield true
if any array element can be cast to an Int
and is equal to the given intValue
.
The might see the first method more frequently but the second is far more general, e.g.
if myArrayOfCustomObjects.contains ({ $0.someProperty == "foo" })
or
if myArrayOfInts.contains ({ $0 > 17 })
So the first contains()
is a kind of specialization of the second one for a simple containment check in arrays of equatable elements. It could be implemented as
extension SequenceType where Generator.Element : Equatable {
public func contains(element: Self.Generator.Element) -> Bool {
return self.contains({ $0 == element } )
}
}
You'll find the same with indexOf()
which also comes in
two flavors:
extension CollectionType where Generator.Element : Equatable {
// ...
public func indexOf(element: Self.Generator.Element) -> Self.Index?
}
extension CollectionType {
// ...
public func indexOf(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Index?
}
来源:https://stackoverflow.com/questions/34081580/array-of-any-and-contains