Add shuffle to Data List and LIST

Bump version to 1.3.0
This commit is contained in:
StableLlama
2026-01-31 19:42:33 +01:00
parent 607093e9cd
commit 443e75be09
6 changed files with 141 additions and 3 deletions
+2 -2
View File
@@ -60,7 +60,7 @@ Mechanisms to direct workflow execution:
ComfyUI list manipulation nodes (for processing individual items):
- **Creation**: create Data List (generic and type-specific versions)
- **Modification**: append, extend, insert, set item, remove, pop, pop random
- **Modification**: append, extend, insert, set item, shuffle, remove, pop, pop random
- **Filtering**: filter, filter select
- **Access**: get item, first, last, slice, index, contains
- **Information**: length, count
@@ -103,7 +103,7 @@ Integer operation nodes:
Python list manipulation nodes (as a single variable):
- **Creation**: create LIST (generic and type-specific versions)
- **Modification**: append, extend, insert, remove, pop, pop random, set_item
- **Modification**: append, extend, insert, remove, pop, pop random, set_item, shuffle
- **Access**: get_item, first, last, slice, index, contains
- **Information**: length, count
- **Operations**: sort, reverse, min, max
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "basic_data_handling"
version = "1.2.0"
version = "1.3.0"
description = """Basic Python functions for manipulating data that every programmer is used to, lightweight with no additional dependencies.
Supported data types:
@@ -920,6 +920,39 @@ class DataListSetItem(ComfyNodeABC):
raise IndexError(f"Index {index} out of range for list of length {len(input_list)}")
class DataListShuffle(ComfyNodeABC):
"""
Shuffles the items in a list using a seed for reproducibility.
This node takes a list and a seed as input and returns a new shuffled list.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": (IO.ANY, {}),
"seed": (IO.INT, {"default": 0}),
}
}
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/Data List"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "shuffle_list"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
def shuffle_list(self, **kwargs: list[Any]) -> tuple[list[Any]]:
import random
input_list = kwargs.get('list', [])
seed = kwargs.get('seed', [0])[0]
random.seed(seed)
result = input_list.copy()
random.shuffle(result)
return (result,)
class DataListSlice(ComfyNodeABC):
"""
Creates a slice of a list.
@@ -1145,6 +1178,7 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: DataListRemove": DataListRemove,
"Basic data handling: DataListReverse": DataListReverse,
"Basic data handling: DataListSetItem": DataListSetItem,
"Basic data handling: DataListShuffle": DataListShuffle,
"Basic data handling: DataListSlice": DataListSlice,
"Basic data handling: DataListSort": DataListSort,
"Basic data handling: DataListSum": DataListSum,
@@ -1183,6 +1217,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: DataListRemove": "remove",
"Basic data handling: DataListReverse": "reverse",
"Basic data handling: DataListSetItem": "set item",
"Basic data handling: DataListShuffle": "shuffle",
"Basic data handling: DataListSlice": "slice",
"Basic data handling: DataListSort": "sort",
"Basic data handling: DataListSum": "sum",
+31
View File
@@ -741,6 +741,35 @@ class ListSetItem(ComfyNodeABC):
except IndexError:
raise IndexError(f"Index {index} out of range for LIST of length {len(list)}")
class ListShuffle(ComfyNodeABC):
"""
Shuffles the items in a list using a seed for reproducibility.
This node takes a LIST and a seed as input and returns a new shuffled LIST.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("LIST", {}),
"seed": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("LIST",)
CATEGORY = "Basic/LIST"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "shuffle_list"
def shuffle_list(self, list: list[Any], seed: int) -> tuple[list[Any]]:
import random
random.seed(seed)
result = list.copy()
random.shuffle(result)
return (result,)
class ListSlice(ComfyNodeABC):
"""
Creates a slice of a LIST.
@@ -910,6 +939,7 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: ListRemove": ListRemove,
"Basic data handling: ListReverse": ListReverse,
"Basic data handling: ListSetItem": ListSetItem,
"Basic data handling: ListShuffle": ListShuffle,
"Basic data handling: ListSlice": ListSlice,
"Basic data handling: ListSort": ListSort,
"Basic data handling: ListSum": ListSum,
@@ -944,6 +974,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: ListRemove": "remove",
"Basic data handling: ListReverse": "reverse",
"Basic data handling: ListSetItem": "set item",
"Basic data handling: ListShuffle": "shuffle",
"Basic data handling: ListSlice": "slice",
"Basic data handling: ListSort": "sort",
"Basic data handling: ListSum": "sum",
+36
View File
@@ -29,6 +29,7 @@ from src.basic_data_handling.data_list_nodes import (
DataListRemove,
DataListReverse,
DataListSetItem,
DataListShuffle,
DataListSlice,
DataListSort,
DataListSum,
@@ -128,6 +129,41 @@ def test_set_item():
node.set_item(list=[], index=[0], value=["test"]) # Out of range
def test_shuffle():
node = DataListShuffle()
# Test determinism: Same seed should produce the same shuffle
original_list = [1, 2, 3, 4, 5]
result1 = node.shuffle_list(list=original_list, seed=[42])
result2 = node.shuffle_list(list=original_list, seed=[42])
assert result1 == result2 # Same seed, same output
# Verify the output is a permutation of the input
assert sorted(result1[0]) == sorted(original_list)
assert len(result1[0]) == len(original_list)
# Test different seeds produce different shuffles (not guaranteed, but likely for this list)
result3 = node.shuffle_list(list=original_list, seed=[123])
assert result1 != result3 # Different seed, likely different output
# Test empty list
assert node.shuffle_list(list=[], seed=[0]) == ([],)
# Test single-item list
assert node.shuffle_list(list=[42], seed=[99]) == ([42],)
# Test mixed data types
mixed_list = ["apple", 3.14, True, 42]
result4 = node.shuffle_list(list=mixed_list, seed=[7])
assert sorted(result4[0], key=str) == sorted(mixed_list, key=str) # Sort for comparison since types may not be directly comparable
assert len(result4[0]) == len(mixed_list)
# Test that the original list is not modified (node should return a copy)
original_copy = original_list.copy()
node.shuffle_list(list=original_list, seed=[1])
assert original_list == original_copy # Original should remain unchanged
def test_contains():
node = DataListContains()
assert node.contains(list=[1, 2, 3], value=[2]) == (True,)
+36
View File
@@ -26,6 +26,7 @@ from src.basic_data_handling.list_nodes import (
ListRemove,
ListReverse,
ListSetItem,
ListShuffle,
ListSlice,
ListSort,
ListSum,
@@ -158,6 +159,41 @@ def test_list_set_item():
node.set_item([1, 2, 3], 3, 42) == ([1, 2, 3],) # Out of range
def test_shuffle():
node = ListShuffle()
# Test determinism: Same seed should produce the same shuffle
original_list = [1, 2, 3, 4, 5]
result1 = node.shuffle_list(list=original_list, seed=42)
result2 = node.shuffle_list(list=original_list, seed=42)
assert result1 == result2 # Same seed, same output
# Verify the output is a permutation of the input
assert sorted(result1[0]) == sorted(original_list)
assert len(result1[0]) == len(original_list)
# Test different seeds produce different shuffles (not guaranteed, but likely for this list)
result3 = node.shuffle_list(list=original_list, seed=123)
assert result1 != result3 # Different seed, likely different output
# Test empty list
assert node.shuffle_list(list=[], seed=0) == ([],)
# Test single-item list
assert node.shuffle_list(list=[42], seed=99) == ([42],)
# Test mixed data types
mixed_list = ["apple", 3.14, True, 42]
result4 = node.shuffle_list(list=mixed_list, seed=7)
assert sorted(result4[0], key=str) == sorted(mixed_list, key=str) # Sort for comparison since types may not be directly comparable
assert len(result4[0]) == len(mixed_list)
# Test that the original list is not modified (node should return a copy)
original_copy = original_list.copy()
node.shuffle_list(list=original_list, seed=1)
assert original_list == original_copy # Original should remain unchanged
def test_list_contains():
node = ListContains()
assert node.contains([1, 2, 3], 2) == (True,)