Swift 2 Array Contains object?
Asked Answered
B

4

10

Why isn't this working? I can use array.contains() on a String but it doesn't work for an Object.

var array = ["A", "B", "C"]

array.contains("A") // True

class Dog {
    var age = 1
}

var dogs = [Dog(), Dog(), Dog()]
var sparky = Dog()
dogs.contains(sparky) // Error Cannot convert value of type 'Dog' to expected argument type '@noescape (Dog) throws -> Bool
Blurb answered 16/2, 2016 at 21:25 Comment(0)
E
13

Your Dog needs to implement Equatable.

class Dog: Equatable {

   var age = 1

}

func == (lhs: Dog, rhs: Dog) -> Bool {
      return lhs.age == rhs.age
}
Expeditious answered 16/2, 2016 at 21:29 Comment(0)
E
10

To really explain what's happening there, first we have to understand there are two contains methods on Array (or better said, on SequenceType).

func contains(_ element: Self.Generator.Element) -> Bool

with constraints

Generator.Element : Equatable

and

func contains(@noescape _ predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Bool

The first one basically searches for a given element in the array using ==. The second one uses a closure that returns a Bool to search for elements.

The first method cannot be used because Dog doesn't adopt Equatable. The compiler tries to use the second method but that one has a closure as the parameter, hence the error you are seeing.

Solution: implement Equatable for Dog.

If you are looking for object reference comparison, you can use a simple closure:

let result = dogs.contains({ $0 === sparky })
Esculent answered 16/2, 2016 at 21:35 Comment(3)
Thanks for the explanation.Blurb
It's not possible to search for the Object reference?Blurb
@user752543 It is, if you use a closure. See my edit.Esculent
A
2

Swift

If you are not using object then you can user this code for contains.

let elements = [ 10, 20, 30, 40, 50]

if elements.contains(50) {

    print("true")

}

If you are using NSObject Class in swift. This variables is according to my requirement. you can modify for your requirement.

var cliectScreenList = [ATModelLeadInfo]()
var cliectScreenSelectedObject: ATModelLeadInfo!

This is for a same data type.

{ $0.user_id == cliectScreenSelectedObject.user_id }

If you want to AnyObject type.

{ "\($0.user_id)" == "\(cliectScreenSelectedObject.user_id)" }

Full condition

if cliectScreenSelected.contains( { $0.user_id == cliectScreenSelectedObject.user_id } ) == false {

cliectScreenSelected.append(cliectScreenSelectedObject)

print("Object Added")

} else {

print("Object already exists")

}
Ardoin answered 2/8, 2016 at 12:54 Comment(0)
L
0

This answer isn't relevant for the OP's question, but might be helpful to others who are confronted with the Swift error message

Cannot invoke 'contains' with an argument list of type '(whatever)'

But first a quick quiz: Can you spot the problem here?

internal class FrameworkAdminConnections {

   private var _localConnectionKeys = [Int]()

   ... other code omitted

   public func isLocalConnection(_ connectionKey : Int) {
      return _localConnectionKeys.contains(connectionKey)
   }
}   

Swift kept telling me I couldn't invoke contains() with an argument list of type (Int), which was a very unhelpful error message, and I don't dare admit how long it took me to finally figure it out.

The real problem was that Swift's inference engine couldn't figure out what the result of the contains() method should be - because I'd stupidly not specified "-> Bool" on the isLocalConnection() method signature!

Laurenalaurence answered 2/3, 2017 at 22:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.