0

I have an app where user can click on other user profile and than app will make comparison between phone contacts of both users and search for common contacts. That works, but it consumes much time so I was wondering is there a way I can speed up that at least a little bit.

****Note I'm beginner in iOS development and still not very familiar with Swift syntax and iOS in general so any help would be very appreciated

Here is my simple Contact class:

class Contact: Hashable, Equatable {
  var hashValue: Int { get { return phoneNumber!.hashValue } }
  var name: String?
  var phoneNumber: String?
  var thumbnail: Data?
  var filled: Bool?

  init() {
  }

  init(name: String?, phoneNumber: String?, thumbnail: Data?) {
    self.name = name
    self.phoneNumber = phoneNumber
    self.thumbnail = thumbnail
}
static func ==(lhs: Contact, rhs: Contact) -> Bool {
    return lhs.phoneNumber == rhs.phoneNumber
  }
}

I have implemented, as you can see, Hashable and Equatable which I use to compare contacts by phone number and remove duplicates. Here is the code where I perform main operation of comparing. contacts array contains phone contacts of user and otherContacts is array of phone contacts of other user.

                for result in self.contacts {
                if self.otherContacts.contains(result){
                    self.commonContacts.append(result)
                }
            }
            self.removedDuplicates = Array(Set(self.commonContacts))
            if self.removedDuplicates.count == 1 {
                self.commonFriends.text = "\(1) common friend"
            }
            else {
                self.commonFriends.text = "\(self.removedDuplicates.count) common friends"
            }

Thanks.

Yupi
  • 4,402
  • 3
  • 18
  • 37
  • Not related to your question but are those properties really optional? Why don't you use a struct? Btw no need to implement the hashValue yourself anymore. `struct Contact: Hashable, Equatable { let name: String let phoneNumber: String let thumbnail: Data let filled: Bool }` thats all you need – Leo Dabus Aug 27 '18 at 22:48
  • about removing duplicates https://stackoverflow.com/questions/25738817/removing-duplicate-elements-from-an-array-in-swift/34712330#34712330 – Leo Dabus Aug 27 '18 at 22:58
  • 1
    Not related to your question, but wouldn't String comparisons on phone numbers result in false negatives? e.g. `"020-12312312" != "02012312312"`. PhoneNumber should probably be a class dealing with this kind of stuff. – TmKVU Aug 27 '18 at 23:03
  • 1
    @LeoDabus thanks I changed to `struct`. – Yupi Aug 27 '18 at 23:08
  • @TmKVU phone numbers comes in same format but that also makes sense if some doesn't come in expected format. Also I was wondering would comparing `integers` be faster than comparing `strings` – Yupi Aug 27 '18 at 23:10
  • How about using `Set` as an internal storage instead of array and then use `intersection` on two sets? – adev Aug 27 '18 at 23:25
  • 1
    @Yupi Yes I do think so, `Strings` are basically arrays of characters, which are represented by integers, hence a comparison of two Strings is most likely implemented as multiple character (integer) comparisons. However, the calculation of the hash value will create overhead. I see you never store the hash value, but calculate it every time it is accessed. Maybe you should change the hashValue property to a [lazy var](https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID257). – TmKVU Aug 28 '18 at 07:49

3 Answers3

2

Is there any reason why contacts is an array? I would use a Set<Contact>, as you can easily perform set operations.

To get the common elements of each set, assuming your implementation of hashcode is correct, simply use Set.intersection(_:)

Schemetrical
  • 5,506
  • 2
  • 26
  • 43
2

For arrays, contains has O(n) performance. So checking all the elements from one array against another array has O(n^2) (n-squared) performance, which is bad.

Copy the phone numbers from one array into a set, and use Set's contains(_:), function, which has very close to constant time performance, giving you a total of O(n) performance, which is much, much better.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
1

You can try merging with a Dictionary, it could give you a boost.

var commonContacts = Dictionary(uniqueKeysWithValues: contacts.lazy.map{ return ($0.phoneNumber!, $0) })
    .merge(otherContacts.lazy.map{ ($0.phoneNumber!, $0) }, uniquingKeysWith: {
        contact1, _ in contact1 // Can do merging of duplicates here.
    })
Fabian
  • 5,040
  • 2
  • 23
  • 35