0

I am a swift newbie and trying to play around with the Contacts framework and have encountered a problem:-

I have an array of CNContact called contacts with keys of givenName, familyName and phoneNumbers. I want to filter out certain names that is defined as type String from the array of contacts

I tried:

for name in namesToRemove { // 'name' is always in fullname format
    contacts = contacts.filter () { $0.givenName + " " + $0.familyName != name }
}

but the removal only works if both givenName and familyName are specified.

Also, keep in mind that on an iPhone device, some contacts' full name were only specified in either its 'First name' or 'Last name' column.

Any ideas how do I go about this? Much appreciated!

allanae
  • 145
  • 2
  • 12

1 Answers1

1

You could make use contains for the namesToRemove array as a conditional for a filter operation on contacts:

let filteredContacts = contacts.filter { !namesToRemove.contains($0.givenName) }

We first setup a CNContact example structure (since you have not supplied us with a minimal working example ...)

/* example setup */
struct CNContact {
    let givenName: String
    let familyName: String
    let phoneNumbers: [String]
    init(_ givenName: String, _ familyName: String, 
         _ phoneNumbers: [String]) { 
        self.givenName = givenName
        self.familyName = familyName
        self.phoneNumbers = phoneNumbers
    }
} 

let contacts = [CNContact("David", "Scott",   ["123-567", "010-111"]),
                CNContact("Lisa",  "Rowling", ["134-521", "121-731"]),
                CNContact("Brad",  "Knight",  ["621-141", "551-723"]),
                CNContact("David", "Phelps",  ["652-723", "718-888"]),
                CNContact("Sarah", "Bright",  ["166-720", "378-743"])]

Example usage:

/* example #1: 
   filter by removing any contacts that whose 'givenName' property
   are contained in a list of given surnames to remove 'namesToRemove' */
let namesToRemove1 = ["David", "Sarah"]
let filteredContacts1 = contacts.filter { !namesToRemove1.contains($0.givenName) }

filteredContacts1.forEach { 
    print($0.givenName, $0.familyName, $0.phoneNumbers.first ?? "none") 
} /* Lisa Rowling 134-521
     Brad Knight 621-141 */

/* example #2: 
   filter by removing any contacts that whose 'givenName' and 'familyName'
   properties are contained in a list of given full names to remove ('namesToRemove'), 
   where we know these full names are separated by exactly a single whitespace */
let namesToRemove2 = ["David Phelps", "Sarah Bright"]
let filteredContacts2 = contacts.filter { 
    !namesToRemove2.contains($0.givenName + " " + $0.familyName) }

filteredContacts2.forEach { 
    print($0.givenName, $0.familyName, $0.phoneNumbers.first ?? "none") 
} /* David Scott 123-567
     Lisa Rowling 134-521
     Brad Knight 621-141  */

Finally, an additional example based on the subsequent updates of your question:

/* example #3: 
   filter by removing any contacts where at least one of the following
   holds: 
   1. 'givenName' + " " + 'familyName' equals a name in 'namesToRemove',
   2. 'givenName' by itself equals a name in 'namesToRemove',
   3. 'familyName' by itself equals a name in 'namesToRemove', */

let contacts2 = [CNContact("David", "Scott",   ["123-567", "010-111"]),
                CNContact("Lisa",  "Rowling", ["134-521", "121-731"]),
                CNContact("",  "Brad Knight",  ["621-141", "551-723"]),
                CNContact("David Phelps", "",  ["652-723", "718-888"]),
                CNContact("Sarah", "Bright",  ["166-720", "378-743"])]
   print(" ")
let namesToRemove3 = ["David Phelps", "Sarah Bright", "Brad Knight"]
let filteredContacts3 = contacts2.filter { 
    !(namesToRemove3.contains($0.givenName + " " + $0.familyName) ||
      namesToRemove3.contains($0.givenName) ||
      namesToRemove3.contains($0.familyName)) }

filteredContacts3.forEach { 
    print($0.givenName, $0.familyName, $0.phoneNumbers.first ?? "none") 
} /* David Scott 123-567
     Lisa Rowling 134-521 */
allanae
  • 145
  • 2
  • 12
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • This is great. But, what if I want to include key familyName as well? taking into account that the contact information saved in local phonebook sometimes only uses the column 'first name' as the full name? Safe to assume that elements in namesToRemove all uses full name (givenName + familyName) – allanae Nov 04 '16 at 10:28
  • @ArezWong I'm not sure I fully follow what you want to achieve. Could you update your question with an actual example of an `contact` array, a `namesToRemove` array, and how a resulting array looks after filtering? Making sure your example takes into account all the use cases you're trying to specify. – dfrib Nov 04 '16 at 10:40
  • Tried this `let filteredContacts = contacts.filter { !namesToRemove.contains($0.givenName + " " + $0.familyName) }`. Works if the contacts' full name is completed have givenName & familyName are combined. – allanae Nov 04 '16 at 10:42
  • Edited the question – allanae Nov 04 '16 at 10:50
  • @ArezWong updated the answer, but note that the solution in example #2 is very fragile, as it depends on knowing that the `namesToRemove` array contains exactly `givenName` and `familyName` on the concenated form `"givenName + " " + "surName"` (but this is what you specified ...). – dfrib Nov 04 '16 at 10:59
  • 1
    @ArezWong Also, please avoid subsequent updating of the question, making it grow as each answer comes along. I'll include a single last update, but this has now grown into a "consider writing this application code for me", rather than pointing you in the right direction w.r.t. how filtering works. – dfrib Nov 04 '16 at 11:01
  • I'm sorry for frequently updating the question, I'm not just new at swift but also new at posting question in here.. but from what you have pointed out, I think I could use it as a guideline to make it work. Thanks anyway! – allanae Nov 04 '16 at 11:06
  • 1
    @ArezWong Included a final example which filters vs the `namesToRemove` array by checking: 1. full name (from `namesToRemove`) match vs concenated `givenName + " " + familyName`, 2. full name (from `namesToRemove`) match vs `givenName`, 3. full name (from `namesToRemove`) match vs `familyName`. – dfrib Nov 04 '16 at 11:09