2

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.

Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
  • You may have to loop through the array, and for each element, if casting that object to an Int is successful, compare. If equal, return true. Otherwise, continue to next element. – DrOverbuild Dec 04 '15 at 05:40
  • I know, that straigh-forward method is guaranteed to work. However, it doesn't sound very Swift-y... – Nicolas Miari Dec 04 '15 at 05:42

1 Answers1

8

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?
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thank you for your through answer; very enlightening. – Nicolas Miari Dec 04 '15 at 07:05
  • So, there is no simple, non-duplicating way to check if the value of a variable defined as `Any` (`Int`, `String` or `Bool`) is contained in an array of type `[Any]`, but that is guaranteed at runtime to contain values **of the same type** as the variable? – Nicolas Miari Dec 08 '15 at 10:06