Check if contact with email exists in contacts - using JXA

I am trying to write a script that checks if contact with given email exists in my contacts.

Code that I tried

function run(){
    const Contacts = Application('Contacts');
    const found_contacts = Contacts.people.emails.whose({value:"email@email.com"})
    return found_contacts.length
}

At this point, I just want to know if found_contacts is non zero.

And would like to be able to access the names of found contacts, I assumed that would be have to be like this found_contacts()[0].name().

But I cannot seem to figure out JXA. :frowning:

The kind of pattern you are trying is often the right one, but bamboozled here because the Contacts records don't have single-string email values.

(Instead each person record includes a (possibly empty) list of email strings).

One approach might look something like this:

(() => {
    "use strict";

    // main :: IO ()
    const main = () => {
        const
            appContacts = Application("Contacts"),
            people = appContacts.people,
            names = people.name(),
            // Each person record has a (possibly empty)
            // *list* of email addresses.
            emailLists = people.emails.value(),
            namesWithEmails = [].concat(
                ...zipWith(
                    name => emailList => 0 < emailList.length ? [{
                        name,
                        emailList
                    }] : []
                )(names)(emailLists)
            );

        return namesWithEmails.flatMap(
            record => record.emailList.includes(
                "email.email.com"
            ) ? (
                [record]
            ) : []
        );
    };

    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
    const zipWith = f =>
        // A list constructed by zipping with a
        // custom function, rather than with the
        // default tuple constructor.
        xs => ys => xs.map(
            (x, i) => f(x)(ys[i])
        ).slice(
            0, Math.min(xs.length, ys.length)
        );

    // MAIN ---
    return main();
})();
1 Like

I kept at it. Seems this below will work. Not sure if this is the right way to do it.

function run(){
    const Contacts = Application('Contacts');
	for (var contact of Contacts.people()){
		var found = contact.emails.whose({value:{_contains:"email@email.com"}})
		if (found.length > 0){
			return contact.name()
		}
	}
}

The way I figured is that Contacts.people is an array. And emails of a contact is also an array.
And if I have to use whose, it cannot be on an array of arrays.
Not sure if that is the right understanding.

1 Like

It should work. May be a little slower (more interface traffic) if your Contacts database is big.

( The example I showed uses one Apple Event to fetch all the names, and another to fetch all the email lists, so it's reasonably economical.

Your working redraft is fine, but it costs an extra Apple Event for every person.

That may not matter much if the database is not huge)

I am in love with your scripts. So elegant. Unfortunately, not able to understand them well. :innocent:
I even tried to learn Haskell some time ago, but got sidetracked and could not complete it.

It be so cool if you did a video about how people can learn this style of programming. I am kidding, grateful enough that you share your expertise here. There is text and videos on functional programming, but not many example js or python scripts with some explanation.

After you observed, I can see how you made two apple event calls, but I guess I will have to read about how the zip then happened, and some of the advanced js code. I will work on understanding your answer.

Thank you!

I will try to resume learning Haskell and need more JS expertise.

But one important question, which was a stumbling block last time.

  • Is there a way one can debug this code using an editor like VS Code say? Where the variables etc can flash on the side and one can create visual breakpoints? @ComplexPoint

Thank you!

You can step through the code in the Safari JS debugger.

In VS Code you need to insert the keyword debugger; somewhere in your source, in a line of its own, to break flow and enter the Safari Web Inspector view:

And Safari needs to be set up to show the Web Inspector view for JSContexts:

  • Under Preferences > Advanced :: Show Develop menu in menu bar
  • Under the Develop menu's third item, which will be the name of your device :: Automatically show Web Inspector for JS Contexts

1 Like

Thank you again for sharing the screenshots and your reply.

Had used this debugger early on with jxa but did understand the concept of object specifier then. I think I understand it better now.

Have a wonderful day.

Are you saying that we can debug JXA code in VSC directly?
How do we "enter the Safari Web Inspector view" in VSC?
I don't see any option for that in the VSC View menu.

Thanks.

You run the code.

(If the Safari settings are in place, the Web Inspector view is launched automatically, with the debugger; keyword as the initial break-point).

Sorry, that's not clear. Run from where?
When I run from VSC (Menu Run) I see this:

image

Both choices open a new Chrome window (even though Safari is my default browser) like this:

image

EDIT:

BTW, I tried to add a new "launch configuration", but there was NOT a choice for "Safari":

image

Suggestions?

Code can be executed from VSC in various ways (see, for example the integrated terminal)

Edit and run code in Visual Studio Code

The Code Runner extension is widely used

[Code Runner]( https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner )

This isn't what you asked for, but here's a short AppleScript that returns a list of contacts containing a given email address, which equals {} if none exist.

tell application "Contacts"
set setOfPeople to (people whose value of emails contains "robert@apple.com")
end tell

:slight_smile:

1 Like

Thanks for sharing.

Learning to transform from AppleScript to JXA is valuable though. More AppleScript solutions exist in the wild and JXA just seems to be more modern language and worth learning therefore.