MacOS – How to find the intersection of two lists in AppleScript

applescriptdevelopmentmacos

Let's say I have two lists in AppleScript:

set listA to {"red", "green", "blue"}
set listB to {"green", "blue"}

I'd like to create a third list that has only the items present in both lists:

set listC to intersection of listA and listB --something like this
-- would return {"green", "blue"}

Basically, something like PHP's array_intersect().

Is there a built-in way to do this?
If not, I'd love a recommendation for a scripting addition that solves this problem.

Best Answer

The List & Record Tools Scripting Addition provides intersection of listA and listB (among other bits of terminology).

There is no built-in “primitive” for computing a list intersection. Usually one would just use one or more loops to check each element of one list against the other. This can be simplied (and sped up1) a bit by using one of the is in/is contained by/contains operators (or a negation thereof):

set listA to {"red", "green", "blue"}
set listB to {"green", "blue"}

to intersection of listA against listB
    local newList, a
    set newList to {}
    repeat with a in listA
        set a to contents of a -- dereference implicit loop reference
        if {a} is in listB then set end of newList to a
    end repeat
    newList
end intersection

intersection of listA against listB --> {"green", "blue"}

1The is in operator and its relatives are implemented by the AppleScript runtime environment in lower-level code where they execute much faster than the normal interpreted AppleScript code.


Incidentally, handlers that use labeled parameters have a limited set of terms that can be used for the labels. For example, intersection of listA and listB is not possible as a user-defined handler because and is not a recognized label. See the AppleScript Language Guide section on handlers with labeled parameters for a list of valid labels. Applications and OSA extensions can defined their own terminologies (e.g. the List & Record OSAX provides this exact syntax), but user-defined handlers are not as flexible. I usually just use positional parameters (handlerName(param1, param2, …)) instead of trying to make invocations of my handlers look like prose.