Writing Phone Numbers to Contacts

I have written a script to reformat the phone numbers that appear in Contacts.the reformatting works fine but the update / “write back” does not work.

The reformatting results in a list called personListReformatted which contains sub-lists for each phone number where each sub-list contains {contact ID, contact last name, contact first name, contact phone number label [i.e. work, work fax, etc.], contact phone number old format, contact phone number new format}.

The final step of the script is to update / “write back” the reformatted telephone numbers. To do this I am using the following script extract within a Contacts tell block [i.e. using the general syntax set value of phones of label of phones of person id to phone number reformatted]:



repeat with m from 1 to (count of personListReformatted)
set value of phones of (item 4 of item m of personListReformatted) of phones of item 1 of item m of personListReformatted to item 6 of item m of personListReformatted
end repeat


While I would have thought that the above script would have work it does not and would appreciate your assistance in getting this to work.

Thanks,

Joel

Hi. If I understand correctly and the list of lists is formatted per your explanation”{id, last, first, label, old, new}”then item 4 is a label and 6 is a phone value, which are incompatible for replacement.

Hi Joel.

With each reformatted number, you need to set the ‘value’ of a particular ‘phone’ of a particular ‘person’. I can’t tell the context from your script snippet. If personListReformatted is a list of details for all the phones of a particular person, then it’s possible that the phones are in the same order in the list as they are in the ‘person’ in Contacts:


-- tell application "Contacts"
	
	repeat with m from 1 to (count of personListReformatted)
		set phoneDetails to item m of personListReformatted
		set value of phone m of person id (item 1 of phoneDetails) to item 6 of phoneDetails
	end repeat
	
-- end tell

Otherwise, to be sure, you could identify each phone by its current value and label before changing its value:


-- tell application "Contacts"
	
	repeat with m from 1 to (count of personListReformatted)
		set phoneDetails to item m of personListReformatted
		set value of (first phone of person id (item 1 of phoneDetails) whose value is (item 5 of phoneDetails) and label is (item 4 of phoneDetails)) to item 6 of phoneDetails
	end repeat
	
-- end tell

Or you could modify your script so that each list in personListReformatted also contains the id of the phone it represents, then identify the phone by that rather than by the current value and label.

The code suggestions above are based on general principles and haven’t been tested in this particular case.

Hi Nigel, appreciate your help once again…as far as you above comment is concerned i) agreed, as I knew that I had to do this and this was exactly what I thought I was doing and ii) I am – as evidenced by this post – continuing to struggle with the syntax.

The sub-lists contained with personListReformatted are per phone number number; that is, the contents of each sublist apply to one phone number so there would be one sublist for Nigel’s office phone number, one sublist for Nigel’s home phone number, etc.

Based on the above I believe that the following script is best:


-- tell application "Contacts"
	
	repeat with m from 1 to (count of personListReformatted)
		set phoneDetails to item m of personListReformatted
		set value of (first phone of person id (item 1 of phoneDetails) whose value is (item 5 of phoneDetails) and label is (item 4 of phoneDetails)) to item 6 of phoneDetails
	end repeat
	
-- end tell

On this points:

  1. Do you agree that the above is the correct code?; and

  2. Why of you need the “first phone” portion of the code as I would surely have missed this?

Not sure what you mean by id of the phone and would appreciate an explanation of this as for each phone number I have contact id, phone label, phone number (pre-reformats) and phone number (post -reformat).

***

As always, greatly appreciate the help and will give it a go later today as I am unfortunately extremely busy at work which is why I have been somewhat absent lately…thanks again and have a good weekend.

If there’s a separate list for each phone number, then you don’t need to filter anything.

tell application "Contacts"
	--set personListreformatted to {{"5CF47367-ADA2-4765-9332-580C62F80839:ABPerson", "Butter", "Cocoa", "work", "0000000000", "000-000-1234"}, {"5CF47367-ADA2-4765-9332-580C62F80839:ABPerson", "Butter", "Cocoa", "home", "0000000000", "000-000-2345"}} --dummy data
	
	
	repeat with personalData in personListreformatted
		set (person id (personalData's item 1))'s phone 1's value to personalData's item 6
	end repeat
	
	--save --must be enabled to see changes in the app
end tell

Hi Marc.

Your script’s essentially the same as my first one in post #3. It uses index references on the assumption that the lists representing the phones in personListReformatted are in the same order as the person’s phones in Contacts. It may in fact be true, but without seeing the rest of Joel’s script, we can’t tell. If the assumption’s not safe, then the phones on the application side must be identified either by filter references or by ID references.

Hi Joel. It’s the safer of the two pieces of code I posted. But see below about ID references.

In a ‘whose’ filter, you have to indicate which match you mean, even when there’s only one matching item (one matching phone in this case). If there is only one matching item ” as I presume is true with your contacts’ phones ” then ‘first phone’, ‘last phone’, ‘some phone’, ‘every phone’ (or ‘phones’), and ‘phones 1 thru -1’ in the whose filter will all mean the same phone (although the plural forms will return it in a list if you’re getting it rather than setting something in it). I always use ‘first’ when I know there’ll only be one match, simply in the hope that it’ll act like an ‘exit repeat’ when the match is found. All the others imply that the filter should carry on looking for further possible matches before coming to a decision.

If you look in Contacts’s dictionary, you’ll see that a ‘phone’ has three properties, inherited from the ‘contact info’ class: its ‘label’, its ‘value’, and its ‘id’. Presumably an earlier part of your script reads the ‘label’ and ‘value’ values from the phones. If it were to read the ‘id’ value as well (or instead of the ‘label’ value), this could be used directly in an ID reference rather than having to use a filter:

tell application "Contacts"
	-- An ID reference .
	phone id bleah of person id blah
	
	-- . rather than a longer (and slower) filter reference:
	first phone of person id blah whose label is this and value is that
end tell

Appreciate this as I think it is sinking in…but to avoid any uncertainty:

  1. As some telephone numbers are the main number for a company / office I do have telephone numbers that appear multiple times.

  2. Is my understanding correct that the code which check / compares the contact id and phone label is there to ensure that the reformatted number is written to the proper location?

Yes, I did also read the person id and the phone label which - as you may note – are in the sublists of personReformattedList.

Thanks

Marc,

Appreciate your post particularly as I am on the steep end of the learning curve…one follow up, how / when do I know when I need to use the SAVE command as this would never have dawned on me.

Thanks so much.

You use save when you are ready to commit to the changes you’ve made in Contacts. If something is wrong, there’s no going back. You may want to test on a small subsample, as I did with the dummy data, before processing however many scores of entries that you have.

The only conceivable reason I can think of to have the lists arranged by individual phones”especially considering how much personal info will be duplicated”is that they were engineered for parity. You probably are right that I can’t trust that here.

Appreciate the response…I have been testing on a small database that I created and will most certainly make a backup of all my contacts before running the script on the full database in case something goes terribly wrong.

Yes. It’s to ensure that the ‘phone’ whose number gets changed is the one corresponding to the current sub-list in personListReformatted. A ‘phone’ in this case obviously doesn’t mean the actual instrument, but an instance of a phone number and label on a contact page in Contacts. (A contact’s called a ‘person’ in Contacts AppleScript, regardless of whether it’s an individual or an organisation.) I presume that although some of your contacts may have multiple instances of the same number or multiple instances of the same label, there won’t be multiple instances of the same combination of number and label. So the contact’s first ‘phone’ which has both a certain ‘value’ and a certain ‘label’ should be unique amongst that contact’s ‘phones’.

As I’ve explained above, a more convenient way to identify a particular ‘phone’ uniquely is by its ‘id’. This can even distinguish between ‘phones’ having the same combination of ‘value’ and ‘label’, though I doubt you’d need to here. Still, a phone’s ‘id’ is unique and unchanging, so using it avoids any unforseen consequences of ‘values’ changing during the checks based on them. For the same reason (and others), I’d also recommend dealing with each ‘phone’ individually and not trying to change all similar numbers in a contact with one instruction.

Thank-you, I appreciate the confirmation…

Correct, no contact / person has multiple the same combinations of label and number

Helpful, I will look into this.

Am not and will not do this…I am dealing with one phone number within each contact separately.

***

Thanks for all the help…

I am ready to try running the script on my entire contact directory as I have tested the script on a subset, I have backed up my contacts, etc…the one question I have is whether I have to invoke the save command i) after each record is written or ii) after all records are written…I suspect it is the second but would appreciate confirmation / correction as appropriate.

Thanks,

Joel

Hi Joel.

Just once at the end should be enough. Good luck. :slight_smile:

Nigel:

Appreciate the response.

I have since run the script and thank to you – and everyone else who has helped me – it appears, subject to some more confirmation , to have worked. :slight_smile:

On to the next project / script!

Thanks,

Joel