Add SET.
Little clean ups.
This commit is contained in:
@@ -30,6 +30,14 @@ These nodes are very lightweight and require no additional dependencies.
|
||||
- Information: length, count
|
||||
- Operations: sort, reverse
|
||||
|
||||
- **SET**: Python set manipulation nodes (as a single variable)
|
||||
- Conversion: convert to data list, to LIST
|
||||
- Creation: any to SET, LIST to SET
|
||||
- Modification: add, remove, discard, pop, clear
|
||||
- Information: length, contains
|
||||
- Set operations: union, intersection, difference, symmetric_difference
|
||||
- Comparison: is_subset, is_superset, is_disjoint
|
||||
|
||||
- **data list**: ComfyUI list manipulation nodes (for processing individual items)
|
||||
- Conversion: convert to LIST
|
||||
- Creation: create_empty
|
||||
@@ -58,9 +66,9 @@ These nodes are very lightweight and require no additional dependencies.
|
||||
- **BOOLEAN**: Boolean logic nodes
|
||||
- Logic operations: and, or, not, xor, nand, nor
|
||||
|
||||
## Understanding LIST vs. data list
|
||||
## Understanding LIST vs. data list vs. SET
|
||||
|
||||
ComfyUI has two different list data types that serve different purposes:
|
||||
ComfyUI has different data types that serve different purposes:
|
||||
|
||||
### 1. LIST datatype
|
||||
- A Python list represented as a **single variable** in the workflow
|
||||
@@ -72,7 +80,17 @@ ComfyUI has two different list data types that serve different purposes:
|
||||
- Passing collections between different parts of your workflow
|
||||
- Complex data storage that shouldn't be split apart
|
||||
|
||||
### 2. data list
|
||||
### 2. SET datatype
|
||||
- A Python set represented as a **single variable** in the workflow
|
||||
- Stores unique values with no duplicates
|
||||
- Supports mathematical set operations (union, intersection, etc.)
|
||||
- Best for:
|
||||
- Eliminating duplicate values
|
||||
- Testing membership efficiently
|
||||
- Set operations (union, difference, etc.)
|
||||
- When element order doesn't matter
|
||||
|
||||
### 3. data list
|
||||
- A native ComfyUI list where **items are processed individually**
|
||||
- Acts like a standard array/list in most programming contexts
|
||||
- Items can be accessed individually by compatible nodes
|
||||
@@ -84,15 +102,23 @@ ComfyUI has two different list data types that serve different purposes:
|
||||
- When your operation needs to work with individual items separately
|
||||
|
||||
### Converting between types
|
||||
- Use `convert to data list` node to transform a LIST into a ComfyUI data list
|
||||
- Use `convert to data list` node to transform a LIST or SET into a ComfyUI data list
|
||||
- Use `convert to LIST` node to transform a ComfyUI data list into a LIST object
|
||||
- Use `LIST to SET` to convert a LIST to a SET (removing duplicates)
|
||||
- Use `SET to LIST` to convert a SET to a LIST
|
||||
|
||||
### When to use which type
|
||||
- Use **LIST** when you need to:
|
||||
- Keep items grouped together as a single entity
|
||||
- Perform operations on the entire collection (sorting, reversing, etc.)
|
||||
- Pass a collection between workflow sections
|
||||
- Prevent automatic iteration over items
|
||||
- Use **LIST** when you need:
|
||||
- Ordered collection with potential duplicates
|
||||
- To preserve insertion order
|
||||
- To access elements by position/index
|
||||
- To use methods like append, extend, etc.
|
||||
|
||||
- Use **SET** when you need:
|
||||
- Collection of unique values (no duplicates)
|
||||
- Fast membership testing (x in set)
|
||||
- Set theory operations (union, intersection)
|
||||
- To eliminate duplicates from data
|
||||
|
||||
- Use **data list** when you need to:
|
||||
- Process each item individually through ComfyUI nodes
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from . import boolean_nodes, data_list_nodes, dict_nodes, float_nodes, int_nodes, list_nodes, string_nodes
|
||||
from . import (boolean_nodes, data_list_nodes, dict_nodes, float_nodes, int_nodes,
|
||||
list_nodes, set_nodes, string_nodes)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_CLASS_MAPPINGS.update(boolean_nodes.NODE_CLASS_MAPPINGS)
|
||||
@@ -7,6 +8,7 @@ NODE_CLASS_MAPPINGS.update(dict_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(float_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(int_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(list_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(set_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(string_nodes.NODE_CLASS_MAPPINGS)
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
@@ -16,4 +18,5 @@ NODE_DISPLAY_NAME_MAPPINGS.update(dict_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(float_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(int_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(list_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(set_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(string_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
|
||||
@@ -2,35 +2,6 @@ from typing import Any
|
||||
|
||||
from inspect import cleandoc
|
||||
|
||||
class DataListConvertToList:
|
||||
"""
|
||||
Converts a ComfyUI data list into a LIST object.
|
||||
|
||||
This node takes a data list input (which is typically a list of items with the same type)
|
||||
and converts it to a LIST object (a Python list as a single variable).
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("*", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("LIST",)
|
||||
CATEGORY = "Basic/data list"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
|
||||
return True
|
||||
|
||||
def convert(self, **kwargs: dict[str, list]) -> tuple[list[Any]]:
|
||||
return (kwargs.get('list', []).copy(),)
|
||||
|
||||
|
||||
class DataListAppend:
|
||||
"""
|
||||
Adds an item to the end of a list.
|
||||
@@ -677,8 +648,36 @@ class DataListZip:
|
||||
return (result,)
|
||||
|
||||
|
||||
class DataListToList:
|
||||
"""
|
||||
Converts a ComfyUI data list into a LIST object.
|
||||
|
||||
This node takes a data list input (which is typically a list of items with the same type)
|
||||
and converts it to a LIST object (a Python list as a single variable).
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("*", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("LIST",)
|
||||
CATEGORY = "Basic/data list"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
|
||||
return True
|
||||
|
||||
def convert(self, **kwargs: dict[str, list]) -> tuple[list[Any]]:
|
||||
return (kwargs.get('list', []).copy(),)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"DataListConvertToList": DataListConvertToList,
|
||||
"DataListAppend": DataListAppend,
|
||||
"DataListExtend": DataListExtend,
|
||||
"DataListInsert": DataListInsert,
|
||||
@@ -697,10 +696,10 @@ NODE_CLASS_MAPPINGS = {
|
||||
"DataListContains": DataListContains,
|
||||
"DataListCreateEmpty": DataListCreateEmpty,
|
||||
"DataListZip": DataListZip,
|
||||
"DataListToList": DataListToList,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DataListConvertToList": "convert to LIST",
|
||||
"DataListAppend": "append",
|
||||
"DataListExtend": "extend",
|
||||
"DataListInsert": "insert",
|
||||
@@ -719,4 +718,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DataListContains": "contains",
|
||||
"DataListCreateEmpty": "create_empty",
|
||||
"DataListZip": "zip",
|
||||
"DataListToList": "convert to LIST",
|
||||
}
|
||||
|
||||
@@ -537,59 +537,6 @@ class DictRemove:
|
||||
return result, False
|
||||
|
||||
|
||||
class AnyToDict:
|
||||
"""
|
||||
Converts compatible data structures to a dictionary.
|
||||
|
||||
This node attempts to convert an input value to a dictionary. Compatible
|
||||
inputs include sequences of key-value pairs, mapping objects, and objects
|
||||
with a to_dict() or as_dict() method.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"input": ("*", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("DICT", "BOOLEAN")
|
||||
RETURN_NAMES = ("dict", "success")
|
||||
CATEGORY = "Basic/DICT"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
|
||||
def convert(self, input: Any) -> tuple[dict, bool]:
|
||||
try:
|
||||
if isinstance(input, dict):
|
||||
return input.copy(), True
|
||||
|
||||
# Try converting from items
|
||||
if hasattr(input, "items"):
|
||||
return dict(input.items()), True
|
||||
|
||||
# Try converting from sequence of pairs
|
||||
if hasattr(input, "__iter__") and not isinstance(input, str):
|
||||
try:
|
||||
result = dict(input)
|
||||
return result, True
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
# Check for to_dict or as_dict methods
|
||||
if hasattr(input, "to_dict") and callable(getattr(input, "to_dict")):
|
||||
return input.to_dict(), True
|
||||
|
||||
if hasattr(input, "as_dict") and callable(getattr(input, "as_dict")):
|
||||
return input.as_dict(), True
|
||||
|
||||
# Failed to convert
|
||||
return {}, False
|
||||
|
||||
except Exception:
|
||||
return {}, False
|
||||
|
||||
|
||||
class DictFilterByKeys:
|
||||
"""
|
||||
Creates a new dictionary with only the specified keys.
|
||||
@@ -772,6 +719,59 @@ class DictCompare:
|
||||
return are_equal, only_in_dict1, only_in_dict2, different_values
|
||||
|
||||
|
||||
class AnyToDict:
|
||||
"""
|
||||
Converts compatible data structures to a dictionary.
|
||||
|
||||
This node attempts to convert an input value to a dictionary. Compatible
|
||||
inputs include sequences of key-value pairs, mapping objects, and objects
|
||||
with a to_dict() or as_dict() method.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"input": ("*", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("DICT", "BOOLEAN")
|
||||
RETURN_NAMES = ("dict", "success")
|
||||
CATEGORY = "Basic/DICT"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
|
||||
def convert(self, input: Any) -> tuple[dict, bool]:
|
||||
try:
|
||||
if isinstance(input, dict):
|
||||
return input.copy(), True
|
||||
|
||||
# Try converting from items
|
||||
if hasattr(input, "items"):
|
||||
return dict(input.items()), True
|
||||
|
||||
# Try converting from sequence of pairs
|
||||
if hasattr(input, "__iter__") and not isinstance(input, str):
|
||||
try:
|
||||
result = dict(input)
|
||||
return result, True
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
# Check for to_dict or as_dict methods
|
||||
if hasattr(input, "to_dict") and callable(getattr(input, "to_dict")):
|
||||
return input.to_dict(), True
|
||||
|
||||
if hasattr(input, "as_dict") and callable(getattr(input, "as_dict")):
|
||||
return input.as_dict(), True
|
||||
|
||||
# Failed to convert
|
||||
return {}, False
|
||||
|
||||
except Exception:
|
||||
return {}, False
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"DictCreate": DictCreate,
|
||||
"DictCreateFromItems": DictCreateFromItems,
|
||||
@@ -792,13 +792,13 @@ NODE_CLASS_MAPPINGS = {
|
||||
"DictMerge": DictMerge,
|
||||
"DictGetKeysValues": DictGetKeysValues,
|
||||
"DictRemove": DictRemove,
|
||||
"AnyToDict": AnyToDict,
|
||||
"DictFilterByKeys": DictFilterByKeys,
|
||||
"DictExcludeKeys": DictExcludeKeys,
|
||||
"DictGetMultiple": DictGetMultiple,
|
||||
"DictInvert": DictInvert,
|
||||
"DictCreateFromLists": DictCreateFromLists,
|
||||
"DictCompare": DictCompare,
|
||||
"AnyToDict": AnyToDict,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -821,11 +821,11 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DictMerge": "merge",
|
||||
"DictGetKeysValues": "get_keys_values",
|
||||
"DictRemove": "remove",
|
||||
"AnyToDict": "any_to_DICT",
|
||||
"DictFilterByKeys": "filter_by_keys",
|
||||
"DictExcludeKeys": "exclude_keys",
|
||||
"DictGetMultiple": "get_multiple",
|
||||
"DictInvert": "invert",
|
||||
"DictCreateFromLists": "from_lists",
|
||||
"DictCompare": "compare",
|
||||
"AnyToDict": "any_to_DICT",
|
||||
}
|
||||
|
||||
@@ -1,32 +1,6 @@
|
||||
from typing import Any
|
||||
from inspect import cleandoc
|
||||
|
||||
class ListConvertToDataList:
|
||||
"""
|
||||
Converts a LIST object into a ComfyUI data list.
|
||||
|
||||
This node takes a LIST object (Python list as a single variable) and
|
||||
converts it to a ComfyUI data list, allowing its items to be processed
|
||||
individually by nodes that accept data lists.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("LIST", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
CATEGORY = "Basic/LIST"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def convert(self, list) -> tuple[list[Any]]:
|
||||
return (list,)
|
||||
|
||||
|
||||
class ListAppend:
|
||||
"""
|
||||
Adds an item to the end of a LIST.
|
||||
@@ -466,6 +440,56 @@ class ListContains:
|
||||
return (value in list,)
|
||||
|
||||
|
||||
class ListToDataList:
|
||||
"""
|
||||
Converts a LIST object into a ComfyUI data list.
|
||||
|
||||
This node takes a LIST object (Python list as a single variable) and
|
||||
converts it to a ComfyUI data list, allowing its items to be processed
|
||||
individually by nodes that accept data lists.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("LIST", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
CATEGORY = "Basic/LIST"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def convert(self, list) -> tuple[list[Any]]:
|
||||
return (list,)
|
||||
|
||||
|
||||
class ListToSet:
|
||||
"""
|
||||
Converts a LIST into a SET.
|
||||
|
||||
This node takes a LIST input and creates a new SET containing all unique elements
|
||||
from the LIST, removing any duplicates.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("LIST", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/LIST"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
|
||||
def convert(self, list: list[Any]) -> tuple[set[Any]]:
|
||||
return (set(list),)
|
||||
|
||||
|
||||
class AnyToList:
|
||||
"""
|
||||
Converts any input datatype into a LIST.
|
||||
@@ -492,7 +516,6 @@ class AnyToList:
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ListConvertToDataList": ListConvertToDataList,
|
||||
"ListAppend": ListAppend,
|
||||
"ListExtend": ListExtend,
|
||||
"ListInsert": ListInsert,
|
||||
@@ -508,11 +531,12 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ListGetItem": ListGetItem,
|
||||
"ListSetItem": ListSetItem,
|
||||
"ListContains": ListContains,
|
||||
"ListToDataList": ListToDataList,
|
||||
"ListToSet": ListToSet,
|
||||
"AnyToList": AnyToList,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ListConvertToDataList": "convert to data list",
|
||||
"ListAppend": "append",
|
||||
"ListExtend": "extend",
|
||||
"ListInsert": "insert",
|
||||
@@ -528,5 +552,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ListGetItem": "get item",
|
||||
"ListSetItem": "set item",
|
||||
"ListContains": "contains",
|
||||
"ListToDataList": "convert to data list",
|
||||
"ListToSet": "convert to SET",
|
||||
"AnyToList": "any to LIST",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,512 @@
|
||||
from typing import Any
|
||||
from inspect import cleandoc
|
||||
|
||||
class SetAdd:
|
||||
"""
|
||||
Adds an item to a SET.
|
||||
|
||||
This node takes a SET and any item as inputs, then returns a new SET
|
||||
with the item added. If the item is already present, the SET remains unchanged.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
"item": ("*", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "add"
|
||||
|
||||
def add(self, set: set[Any], item: Any) -> tuple[set[Any]]:
|
||||
result = set.copy()
|
||||
result.add(item)
|
||||
return (result,)
|
||||
|
||||
|
||||
class SetRemove:
|
||||
"""
|
||||
Removes an item from a SET.
|
||||
|
||||
This node takes a SET and any item as inputs, then returns a new SET
|
||||
with the item removed and a success indicator. If the item is not present,
|
||||
the original SET is returned with success set to False.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
"item": ("*", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET", "BOOLEAN")
|
||||
RETURN_NAMES = ("set", "success")
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "remove"
|
||||
|
||||
def remove(self, set: set[Any], item: Any) -> tuple[set[Any], bool]:
|
||||
result = set.copy()
|
||||
try:
|
||||
result.remove(item)
|
||||
return result, True
|
||||
except KeyError:
|
||||
return result, False
|
||||
|
||||
|
||||
class SetDiscard:
|
||||
"""
|
||||
Removes an item from a SET if it is present.
|
||||
|
||||
This node takes a SET and any item as inputs, then returns a new SET
|
||||
with the item removed. Unlike remove, no error is raised if the item is not present.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
"item": ("*", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "discard"
|
||||
|
||||
def discard(self, set: set[Any], item: Any) -> tuple[set[Any]]:
|
||||
result = set.copy()
|
||||
result.discard(item)
|
||||
return (result,)
|
||||
|
||||
|
||||
class SetPop:
|
||||
"""
|
||||
Removes and returns an arbitrary item from a SET.
|
||||
|
||||
This node takes a SET as input and returns both the new SET
|
||||
with an arbitrary item removed and the removed item.
|
||||
When the SET is empty, the item is None.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET", "*")
|
||||
RETURN_NAMES = ("set", "item")
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "pop"
|
||||
|
||||
def pop(self, set: set[Any]) -> tuple[set[Any], Any]:
|
||||
result = set.copy()
|
||||
try:
|
||||
item = result.pop()
|
||||
return result, item
|
||||
except KeyError:
|
||||
return result, None
|
||||
|
||||
|
||||
class SetClear:
|
||||
"""
|
||||
Removes all items from a SET.
|
||||
|
||||
This node takes a SET as input and returns a new empty SET.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "clear"
|
||||
|
||||
def clear(self, set: set[Any]) -> tuple[set[Any]]:
|
||||
return (set.__class__(),)
|
||||
|
||||
|
||||
class SetUnion:
|
||||
"""
|
||||
Returns the union of two or more SETs.
|
||||
|
||||
This node takes multiple SETs as input and returns a new SET containing
|
||||
all elements from all the input SETs.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set1": ("SET", {}),
|
||||
"set2": ("SET", {}),
|
||||
},
|
||||
"optional": {
|
||||
"set3": ("SET", {}),
|
||||
"set4": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "union"
|
||||
|
||||
def union(self, set1: set[Any], set2: set[Any], set3=None, set4=None) -> tuple[set[Any]]:
|
||||
result = set1.copy()
|
||||
result.update(set2)
|
||||
|
||||
if set3 is not None:
|
||||
result.update(set3)
|
||||
|
||||
if set4 is not None:
|
||||
result.update(set4)
|
||||
|
||||
return (result,)
|
||||
|
||||
|
||||
class SetIntersection:
|
||||
"""
|
||||
Returns the intersection of two or more SETs.
|
||||
|
||||
This node takes multiple SETs as input and returns a new SET containing
|
||||
only elements present in all input SETs.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set1": ("SET", {}),
|
||||
"set2": ("SET", {}),
|
||||
},
|
||||
"optional": {
|
||||
"set3": ("SET", {}),
|
||||
"set4": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "intersection"
|
||||
|
||||
def intersection(self, set1: set[Any], set2: set[Any], set3=None, set4=None) -> tuple[set[Any]]:
|
||||
result = set1.copy()
|
||||
result.intersection_update(set2)
|
||||
|
||||
if set3 is not None:
|
||||
result.intersection_update(set3)
|
||||
|
||||
if set4 is not None:
|
||||
result.intersection_update(set4)
|
||||
|
||||
return (result,)
|
||||
|
||||
|
||||
class SetDifference:
|
||||
"""
|
||||
Returns the difference between two SETs.
|
||||
|
||||
This node takes two SETs as input and returns a new SET containing
|
||||
elements in the first SET but not in the second SET.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set1": ("SET", {}),
|
||||
"set2": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "difference"
|
||||
|
||||
def difference(self, set1: set[Any], set2: set[Any]) -> tuple[set[Any]]:
|
||||
result = set1.copy()
|
||||
result.difference_update(set2)
|
||||
return (result,)
|
||||
|
||||
|
||||
class SetSymmetricDifference:
|
||||
"""
|
||||
Returns the symmetric difference between two SETs.
|
||||
|
||||
This node takes two SETs as input and returns a new SET containing
|
||||
elements in either SET but not in both.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set1": ("SET", {}),
|
||||
"set2": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "symmetric_difference"
|
||||
|
||||
def symmetric_difference(self, set1: set[Any], set2: set[Any]) -> tuple[set[Any]]:
|
||||
result = set1.copy()
|
||||
result.symmetric_difference_update(set2)
|
||||
return (result,)
|
||||
|
||||
|
||||
class SetIsSubset:
|
||||
"""
|
||||
Checks if set1 is a subset of set2.
|
||||
|
||||
This node takes two SETs as input and returns True if set1 is a subset of set2
|
||||
(all elements in set1 are also in set2).
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set1": ("SET", {}),
|
||||
"set2": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ("is_subset",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "is_subset"
|
||||
|
||||
def is_subset(self, set1: set[Any], set2: set[Any]) -> tuple[bool]:
|
||||
return (set1.issubset(set2),)
|
||||
|
||||
|
||||
class SetIsSuperset:
|
||||
"""
|
||||
Checks if set1 is a superset of set2.
|
||||
|
||||
This node takes two SETs as input and returns True if set1 is a superset of set2
|
||||
(set1 contains all elements in set2).
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set1": ("SET", {}),
|
||||
"set2": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ("is_superset",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "is_superset"
|
||||
|
||||
def is_superset(self, set1: set[Any], set2: set[Any]) -> tuple[bool]:
|
||||
return (set1.issuperset(set2),)
|
||||
|
||||
|
||||
class SetIsDisjoint:
|
||||
"""
|
||||
Checks if two SETs have no elements in common.
|
||||
|
||||
This node takes two SETs as input and returns True if they have no elements in common.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set1": ("SET", {}),
|
||||
"set2": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ("is_disjoint",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "is_disjoint"
|
||||
|
||||
def is_disjoint(self, set1: set[Any], set2: set[Any]) -> tuple[bool]:
|
||||
return (set1.isdisjoint(set2),)
|
||||
|
||||
|
||||
class SetContains:
|
||||
"""
|
||||
Checks if a SET contains a specified value.
|
||||
|
||||
This node takes a SET and a value as inputs, then returns True if the value
|
||||
is present in the SET, and False otherwise.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
"value": ("*", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ("contains",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "contains"
|
||||
|
||||
def contains(self, set: set[Any], value: Any) -> tuple[bool]:
|
||||
return (value in set,)
|
||||
|
||||
|
||||
class SetLength:
|
||||
"""
|
||||
Returns the number of items in a SET.
|
||||
|
||||
This node takes a SET as input and returns its length (number of elements) as an integer.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("length",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "length"
|
||||
|
||||
def length(self, set: set[Any]) -> tuple[int]:
|
||||
return (len(set),)
|
||||
|
||||
|
||||
class ListToSet:
|
||||
"""
|
||||
Converts a LIST into a SET.
|
||||
|
||||
This node takes a LIST input and creates a new SET containing all unique elements
|
||||
from the LIST, removing any duplicates.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("LIST", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SET",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
|
||||
def convert(self, list: list[Any]) -> tuple[set[Any]]:
|
||||
return (set(list),)
|
||||
|
||||
|
||||
class SetToDataList:
|
||||
"""
|
||||
Converts a SET object into a ComfyUI data list.
|
||||
|
||||
This node takes a SET object (Python set as a single variable) and
|
||||
converts it to a ComfyUI data list, allowing its items to be processed
|
||||
individually by nodes that accept data lists.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def convert(self, set) -> tuple[list[Any]]:
|
||||
return (list(set),)
|
||||
|
||||
|
||||
class SetToList:
|
||||
"""
|
||||
Converts a SET into a LIST.
|
||||
|
||||
This node takes a SET input and creates a new LIST containing all elements
|
||||
from the SET. Note that the order of elements in the resulting LIST is arbitrary
|
||||
since SETs are unordered collections.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"set": ("SET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("LIST",)
|
||||
CATEGORY = "Basic/SET"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "convert"
|
||||
|
||||
def convert(self, set: set[Any]) -> tuple[list[Any]]:
|
||||
return (list(set),)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SetAdd": SetAdd,
|
||||
"SetRemove": SetRemove,
|
||||
"SetDiscard": SetDiscard,
|
||||
"SetPop": SetPop,
|
||||
"SetClear": SetClear,
|
||||
"SetUnion": SetUnion,
|
||||
"SetIntersection": SetIntersection,
|
||||
"SetDifference": SetDifference,
|
||||
"SetSymmetricDifference": SetSymmetricDifference,
|
||||
"SetIsSubset": SetIsSubset,
|
||||
"SetIsSuperset": SetIsSuperset,
|
||||
"SetIsDisjoint": SetIsDisjoint,
|
||||
"SetContains": SetContains,
|
||||
"SetLength": SetLength,
|
||||
"SetToDataList": SetToDataList,
|
||||
"SetToList": SetToList,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SetAdd": "add",
|
||||
"SetRemove": "remove",
|
||||
"SetDiscard": "discard",
|
||||
"SetPop": "pop",
|
||||
"SetClear": "clear",
|
||||
"SetUnion": "union",
|
||||
"SetIntersection": "intersection",
|
||||
"SetDifference": "difference",
|
||||
"SetSymmetricDifference": "symmetric_difference",
|
||||
"SetIsSubset": "is_subset",
|
||||
"SetIsSuperset": "is_superset",
|
||||
"SetIsDisjoint": "is_disjoint",
|
||||
"SetContains": "contains",
|
||||
"SetLength": "length",
|
||||
"SetToDataList": "convert to data list",
|
||||
"SetToList": "convert to LIST",
|
||||
}
|
||||
Reference in New Issue
Block a user