diff --git a/README.md b/README.md index c82bd61..005c97b 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,11 @@ - Outputs: `item`, `current_index`, `total_items` - Returns the current array item and position, with `mode` set to `fixed`, `incr`, or `decr`. +- `Dumas JSON Object Iterator` + - Inputs: `json_input`, `index`, `mode` + - Outputs: `key`, `value`, `current_index`, `total_items` + - Returns the current object entry in insertion order, with `mode` set to `fixed`, `incr`, or `decr`. + All nodes live in the `Dumas/JSON` category. ## Installation @@ -90,6 +95,8 @@ incr -> use index + 1 decr -> use index - 1 ``` +`Dumas JSON Object Iterator` uses the same index rules and iterates object entries in their existing key order. + ## Roadmap This repo is intended to grow into a broader set of Dumas-branded generic utility nodes, including JSON helpers and adjacent data-manipulation tools. diff --git a/dumas_json_nodes.py b/dumas_json_nodes.py index 044e7c1..3f13f19 100644 --- a/dumas_json_nodes.py +++ b/dumas_json_nodes.py @@ -270,6 +270,36 @@ class DumasJSONArrayIteratorNode: return (json_input[current_index], current_index, total_items) +class DumasJSONObjectIteratorNode: + CATEGORY = "Dumas/JSON" + RETURN_TYPES = ("STRING", "JSON", "INT", "INT") + RETURN_NAMES = ("key", "value", "current_index", "total_items") + FUNCTION = "iterate" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "json_input": ("JSON",), + "index": ("INT", {"default": 0, "min": 0, "step": 1}), + "mode": (["fixed", "incr", "decr"], {"default": "fixed"}), + } + } + + def iterate(self, json_input, index, mode): + if not isinstance(json_input, dict): + return (None, None, -1, 0) + + items = list(json_input.items()) + total_items = len(items) + if total_items == 0: + return (None, None, -1, 0) + + current_index = _resolve_iteration_index(index, mode, total_items) + key, value = items[current_index] + return (key, value, current_index, total_items) + + NODE_CLASS_MAPPINGS = { "DumasJSONStringToObject": DumasJSONStringToObjectNode, "DumasJSONObjectToString": DumasJSONObjectToStringNode, @@ -279,6 +309,7 @@ NODE_CLASS_MAPPINGS = { "DumasJSONKeys": DumasJSONKeysNode, "DumasJSONArrayLength": DumasJSONArrayLengthNode, "DumasJSONArrayIterator": DumasJSONArrayIteratorNode, + "DumasJSONObjectIterator": DumasJSONObjectIteratorNode, } NODE_DISPLAY_NAME_MAPPINGS = { @@ -290,4 +321,5 @@ NODE_DISPLAY_NAME_MAPPINGS = { "DumasJSONKeys": "Dumas JSON Keys", "DumasJSONArrayLength": "Dumas JSON Array Length", "DumasJSONArrayIterator": "Dumas JSON Array Iterator", + "DumasJSONObjectIterator": "Dumas JSON Object Iterator", } diff --git a/tests/test_dumas_json_nodes.py b/tests/test_dumas_json_nodes.py index c55c61a..55fdbf5 100644 --- a/tests/test_dumas_json_nodes.py +++ b/tests/test_dumas_json_nodes.py @@ -6,6 +6,7 @@ from dumas_json_nodes import ( DumasJSONGetValueNode, DumasJSONKeysNode, DumasJSONMergeObjectsNode, + DumasJSONObjectIteratorNode, DumasJSONObjectToStringNode, DumasJSONStringToObjectNode, DumasJSONSetValueNode, @@ -109,6 +110,41 @@ class DumasJSONNodeTests(unittest.TestCase): self.assertEqual(result, (None, -1, 0)) + def test_object_iterator_fixed_mode_uses_given_index(self): + node = DumasJSONObjectIteratorNode() + + result = node.iterate({"first": "a", "second": "b", "third": "c"}, 1, "fixed") + + self.assertEqual(result, ("second", "b", 1, 3)) + + def test_object_iterator_incr_mode_advances_index(self): + node = DumasJSONObjectIteratorNode() + + result = node.iterate({"first": "a", "second": "b", "third": "c"}, 1, "incr") + + self.assertEqual(result, ("third", "c", 2, 3)) + + def test_object_iterator_decr_mode_decrements_index(self): + node = DumasJSONObjectIteratorNode() + + result = node.iterate({"first": "a", "second": "b", "third": "c"}, 1, "decr") + + self.assertEqual(result, ("first", "a", 0, 3)) + + def test_object_iterator_clamps_out_of_range_index(self): + node = DumasJSONObjectIteratorNode() + + result = node.iterate({"first": "a", "second": "b", "third": "c"}, 99, "fixed") + + self.assertEqual(result, ("third", "c", 2, 3)) + + def test_object_iterator_handles_empty_objects(self): + node = DumasJSONObjectIteratorNode() + + result = node.iterate({}, 0, "fixed") + + self.assertEqual(result, (None, None, -1, 0)) + if __name__ == "__main__": unittest.main()