0

I’m using JavaScript with osascript, on MacOS Mojave 10.14.3, to write a script that displays people and their contact information from Contacts. I’m adding a function to show all of the people in a particular city. Here’s a simplified version that runs in Script Editor:

contacts = Application('Contacts').people;
matchingAddresses = contacts.addresses.whose({city: "San Diego"});

var addressIds = [];
for (var matchedAddress of matchingAddresses()) {
    if (matchedAddress.length > 0) {
        addressIds.push(matchedAddress[0].id());
    }
}

//loop contacts and get those with a matching address
var personIds = [];
for (var possiblePerson of contacts()) {
    for (var addressToCheck of possiblePerson.addresses()) {
        if (addressIds.includes(addressToCheck.id())) {
            var personId = possiblePerson.id();
            if (!personIds.includes(personId)) {
                personIds.push(personId);
            }
        }
    }
}

personIds.length;

I have an AppleScript test version that is slightly more efficient. Rather than looping through all contacts, it loops through the matching addresses and gets people whose id of addresses contains addressId:

tell application "Contacts"
    set matchingAddresses to (every address where its city is "San Diego") of every person

    set personIds to {}
    repeat with matchedAddressList in matchingAddresses
        repeat with possibleAddress in items of contents of matchedAddressList
            set addressId to id of possibleAddress
            set matchingPerson to first item of (people whose id of addresses contains addressId)
            if not (personIds contains id of matchingPerson) then
                set end of personIds to id of matchingPerson
            end if
        end repeat
    end repeat

    count of personIds
end tell

The AppleScript (people whose id of addresses contains addressId) always returns one person, because address ids are unique and therefore only one person’s list of addresses can contain any particular address id.

If there are better ways in JavaScript or AppleScript to get people from Contacts by the city of one of their addresses, I’d be interested in that.

But my question is, is there a way to duplicate the functionality of first item of (people whose id of addresses contains addressId) using JavaScript, so as to get the one person who has an address matching that address id?

Jerry Stratton
  • 3,287
  • 1
  • 22
  • 30

1 Answers1

0

Maybe something more like…

const contacts = Application('Contacts').people,
    matchingAddresses = contacts.addresses.whose({city: "San Diego"}),
    addressIds = matchingAddresses().filter(a=>a.length).map(a=>a[0].id());

addressIds[0]; // the first id

Or if you want the address…

const contacts = Application('Contacts').people,
    address = contacts.addresses.whose({city: "San Diego"})().filter(a=>a.length)[0]

address
JakeCigar
  • 603
  • 6
  • 12
  • Thanks; what I’m really looking for is the person who contains that address (ultimately, all people in that city). Am I correct that your code rewrites the section of my example that collects the addresses, but in fewer lines? Is there significance to using the comma as a line end delimiter rather than a semicolon? The indentation makes me think there is, but there doesn’t appear to be any difference in code function or execution time if I replace the commas with semicolons. – Jerry Stratton Mar 20 '19 at 18:46
  • 1
    A comma continues the const declaration. I declared 3 const(s). A const is like a var, but once assigned, it may never be changed. It is not a line delimiter. Replacing commas by semicolons changes the 2nd and 3rd into undeclared vars. It’s not good. But it still works. JXA programmers are a lot more lax about declaring. I come from many years of javaScript, I prefer to declare var or const. – JakeCigar Mar 21 '19 at 21:21