_.intersection
What's in every one of these lists?
- py_list
- arrays
- LIST
_.intersection takes several lists and returns the values that show up in all of them. It's set theory as a node: "which tags are common to every workflow?" "Which seeds appear in every batch?" "Which model names exist in all three of these checkpoint lists?" If you've reached for Python's set_a & set_b, you know the operation - this makes it graph-native and keeps your lists as lists.
How it works
The port is honest about its strategy - it leans on sets, then falls back gracefully:
def intersection(self, *args):
try:
setobj = set(self.obj)
for v in args:
setobj = setobj & set(v)
return self._wrap(list(setobj))
except TypeError:
result = _values(self.obj)
for i, v in enumerate(args):
for item in result:
if item not in v:
result.remove(item)
return self._wrap(result)
Fast path: convert everything to sets and intersect. If that throws a TypeError (something unhashable - a list, a dict - snuck in), it falls back to a membership-based scan that removes non-matches one by one. Either way you get back a list, not a set, so it stays ComfyUI-friendly.
The inputs that matter
- py_list - the primary input (
*), the first array. Also accepts a_.CHAINto continue chaining. - arrays - an
*input for the rest of the arrays. This is the one that trips people up: the tooltip says to provide multiple values as a JSON array, like[["a","b"],["b","c"]]. So the first list comes in onpy_list, and every additional list nests inside one JSON value.
Output is a single LIST of the values present in every input list, with duplicates collapsed.
Where you'd actually use it
Anywhere you're comparing collections across sources. A practical one: you have several lists of allowed/denied values from different configs and you want the intersection - the tags that pass every filter. Or, inverted thinking: intersection gives you "safe in all contexts," which is a nice complement to _.union (everything, deduped) and _.difference (in the first but not the others). The three of them cover most list-comparison needs.
One thing to know: because it's set-based on the fast path, order isn't guaranteed and duplicates collapse. If you need to preserve original order or duplicates, this isn't the node.
Installing
Part of comfy-ovum (MIT). ComfyUI Manager → search comfy-ovum → install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/sfinktah/comfy-ovum
The underscore nodes run on the underscore3 port bundled in the repo - no extra install. (Pack-wide, requirements.txt pulls pymediainfo - system libmediainfo on Linux - plus tomli-w, requests, braceexpand, pillow, numpy, markdown-it-py, aiohttp.)
Gotchas
Two practical notes. First, remember the arrays input wants JSON - pasting ["b","c"] without the outer brackets feeds it one array, not a list of arrays, and you'll intersect with a string instead of a list. Second, the pack's README stamps the whole underscore family as WIP and not production-ready. For pure-data comparisons on modest lists, _.intersection behaves; just don't bet a nightly batch job on it.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| py_listopt | * | Primary input object (expected array). You can still pass any JSON-serializable value. Also accepts _.CHAIN to continue chaining. | |
| arraysopt | * | arrays: JSON allowed for arrays/objects where applicable. For multiple values, provide a JSON array (e.g., [1,2,3]). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| LIST | LIST | — |