Home > database >  Is there a good solution for finding contacts duplicating by number
Is there a good solution for finding contacts duplicating by number

Time:11-30

I tried to fetch duplicates using this code

 var contactsByNumber = [CNLabeledValue<CNPhoneNumber>:[CNContact]]()
                
 try self.store.enumerateContacts(with: fetchRequest) { [weak self] contact, stopPointerIfYouWantToStopEnumeration in
                         
                    //Check contact for matching number with other contacts
                    for contactNumber in contact.phoneNumbers {
                        var contactsForNumber = contactsByNumber[contactNumber,default: []]
                        contactsForNumber.append(contact)
                        contactsByNumber[contactNumber] = contactsForNumber
                        print("Contacts count for number \(contactNumber.value.stringValue): \(contactsByNumber[contactNumber]!.count)")
                        print("Owner \(formatter.string(from: contact))")
                    }
 }

And I also have created two contacts with the same number but it prints out that there is only one contact for each number in console:

Contacts count for number (408) 555-3514: 1
Owner Optional("Daniel Higgins Jr.")
Contacts count for number 888-555-5512: 1
Owner Optional("John Appleseed")
Contacts count for number 888-555-1212: 1
Owner Optional("John Appleseed")
Contacts count for number 555-522-8243: 1
Owner Optional("Anna Haro")
Contacts count for number (555) 766-4823: 1
Owner Optional("Hank M. Zakroff")
Contacts count for number (707) 555-1854: 1
Owner Optional("Hank M. Zakroff")
Contacts count for number 555-610-6679: 1
Owner Optional("David Taylor")
Contacts count for number  7 (898) 222-55-22: 1
Owner Optional("FirstMatchingNumber")
Contacts count for number  7 (898) 222-55-22: 1
Owner Optional("SecondMatchingNumber")

CodePudding user response:

CNLabeledValue's implementation of Hashable doesn't take the contained value into account. Each instance has a unique hash value. You should use the .value instead, which will give you a CNPhoneNumber, which does seem to take the value into account when hashing, meaning your entries will be assigned to the same dictionary key:

let number = CNLabeledValue<CNPhoneNumber>(label: nil, value: CNPhoneNumber(stringValue: "123454"))
let number2 = CNLabeledValue<CNPhoneNumber>(label: nil, value: CNPhoneNumber(stringValue: "123454"))
print(number.hashValue == number2.hashValue) // false
print(number.value.hashValue == number2.value.hashValue) //true

So your code should be:

var contactsByNumber = [CNPhoneNumber:[CNContact]]()
                
try self.store.enumerateContacts(with: fetchRequest) { [weak self] contact, stopPointerIfYouWantToStopEnumeration in
                         
    //Check contact for matching number with other contacts
    for contactNumber in contact.phoneNumbers.map({ $0.value }) {
        var contactsForNumber = contactsByNumber[contactNumber,default: []]
        contactsForNumber.append(contact)
        contactsByNumber[contactNumber] = contactsForNumber
        print("Contacts count for number \(contactNumber.stringValue): \(contactsByNumber[contactNumber]!.count)")
                        print("Owner \(formatter.string(from: contact))")
    }
}
  • Related