How do I create a Set of custom objects (Swift)?
Solution 1
To make set of Person
you need to make it conform to Equatable and Hashable protocols:
class Person: Equatable, Hashable {
var Id: Int
var Name: String
init(id: Int, name: String?) {
self.Id = id
self.Name = name ?? ""
}
var hashValue: Int {
get {
return Id.hashValue << 15 + Name.hashValue
}
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.Id == rhs.Id && lhs.Name == rhs.Name
}
Then you can use set of persons like this:
var set = Set<Person>()
set.insert(Person(id: 1, name: "name"))
Solution 2
With Swift 2.0, Hashable and Equitable is a part of NSObject. All you need to do is to override "isEqual" and "var hash:" for the property of interest. In this case: "Id", Set will exclude Person-objects with identical Ids.
class Person: NSObject {
var Id: Int
var Name: String
init(id: Int, name: String?) {
self.Id = id
self.Name = name ?? ""
}
override var hash: Int {
return Id.hashValue
}
override func isEqual(object: AnyObject?) -> Bool {
guard let rhs = object as? Person else {
return false
}
let lhs = self
return lhs.Id == rhs.Id
}
}
func mergeArrays(){
let person1 = Person(id: 1, name: "Tom")
let person2 = Person (id: 2, name: "John")
let person3 = Person(id: 3, name: "Adam")
let downloadedPeople = [person1,person2] //[{NSObject, Id 1, Name "Tom"}, {NSObject, Id 2, Name "John"}]
let peopleStoredLocally = [person1,person3] //[{NSObject, Id 1, Name "Tom"}, {NSObject, Id 3, Name "Adam"}]
let downloadedPeopleSet = Set(downloadedPeople) //{{NSObject, Id 2, Name "John"}, {NSObject, Id 1, Name "Tom"}}
let mergedSet = downloadedPeopleSet.union(peopleStoredLocally) //{{NSObject, Id 2, Name "John"}, {NSObject, Id 3, Name "Adam"}, {NSObject, Id 1, Name "Tom"}}
let mergedArray = Array(mergedSet)//[{NSObject, Id 2, Name "John"}, {NSObject, Id 3, Name "Adam"}, {NSObject, Id 1, Name "Tom"}]
}
Solution 3
UPDATE
Depretation warning when using hashValue:
'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'Person' to 'Hashable' by implementing 'hash(into:)' instead
Following the object Person example, nowadays implementation would be:
class Person: Equatable, Hashable {
let id: Int
let countryId: Int
var name: String
init(id: Int, countryId: Int, name: String) {
self.id = id
self.countryId = countryId
self.name = name
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(countryId)
}
static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.id == rhs.id && lhs.countryId == rhs.countryId
}
}
Note: As per documentation the components used for hashing must be the same as the components compared in your type’s == operator implementation.
![Admin](/assets/logo_square_200-5d0d61d6853298bd2a4fe063103715b4daf2819fc21225efa21dfb93e61952ea.png)
Admin
Updated on July 09, 2022Comments
-
Admin almost 2 years
For my iOS app I have a model something like
class Person { var Id: Int var Name: String init(id: Int, name: String?) { self.Id = id self.Name = name ?? "" } }
Then later on in my
ViewController
when I load data from the server I add some people to an arrayclass ViewController: UIViewController { var people:[Person] = [] override func viewDidLoad() { self.loadPeople() } func loadPeople() { // This data will be coming from a server request // so is just sample. It could have users which // already exist in the people array self.people.append(Person(id: "1", name: "Josh")) self.people.append(Person(id: "2", name: "Ben")) self.people.append(Person(id: "3", name: "Adam")) } }
What I am now trying todo is turn the
people
array into aSet<Person>
so it will not add duplicates. Is this possible to do or do I need to change my logic? -
Vatsal Manot almost 9 yearsI personally use
XOR
to combine hashes, but +1 for including theHashable
protocol in your answer. -
Martin R almost 9 yearsHashable inherits from Equatable, so declaring the class as
class Person: Hashable { ... }
would suffice. Of course, adding Equatable explicitly does no harm. -
Admin almost 9 years@egor.zhdan could you explain the shift bits 15 part and why you used 15 there?
-
egor.zhdan almost 9 years@lennard I'm using bits shift because it (probably) reduces hash collisions. There is no strong reason why using 15 - you can use
Id.hashValue << 30 + Name.hashValue
and it will work. -
herby almost 8 yearsYou should probably not use objC types for a swift app unless you really have to. So i'd recommend using the standalone protocols instead of inheriting from NSObject.