Dynamo uses untyped multi-dimensional arrays, which is flexible but can cause mismatches when two lists fed into a node have different lengths or nesting structures. This custom Python node — Levelize List — makes two lists structurally identical so they can be zipped element-by-element without length errors.
How It Works
The node accepts two lists and four fill-mode flags (only one should be true at a time):
- FillGapsWithNone — pads the shorter list with
None. - FillGapsWithNextListItems — pads with the paired list's corresponding item.
- FillGapsWithFixedValue — pads with a constant value from input IN[6].
- FillGapsWithLastItem — pads with the last seen item from the other list.
The algorithm recurses through both lists in parallel. At each depth level it walks the minimum shared length first, then fills in the extra elements from whichever list is longer according to the selected mode:
python
import re
def isIterable(unit):
"""Return True for Python iterables AND .NET arrays (System.Array)."""
if isinstance(unit, (tuple, set, list, dict)):
return True
if hasattr(unit, "__iter__"):
if re.search("System.Array", str(unit.__iter__())):
return True
return False
def matchElementForList(element, listRef):
"""Mirror the structure of listRef, filling every leaf slot with element."""
if isIterable(listRef):
return [matchElementForList(element, item) for item in listRef]
return element
log = []
lastItemL1 = None
lastItemL2 = None
lastItem = None
def operate(List1, List2, fillCondition, fixedFillValue):
"""
Recursively walk both lists in parallel, pairing elements at each depth.
When one list is shorter, fill the gap according to fillCondition:
0 = fill with None
1 = fill with the paired list's next element
2 = fill with fixedFillValue
3 = fill with the last seen item from the other list
Returns [resultLeft, resultRight].
"""
global lastItemL1, lastItemL2, lastItem
if not isIterable(List1) and not isIterable(List2):
lastItemL1 = List1
lastItemL2 = List2
return [List1, List2]
if not isIterable(List1):
lastItemL1 = List1
ret = [matchElementForList(List1, List2), List2]
lastItemL2 = lastItem
return ret
if not isIterable(List2):
lastItemL2 = List2
ret = [List1, matchElementForList(List2, List1)]
lastItemL1 = lastItem
return ret
minlen = min(len(List1), len(List2))
left = []
right = []
for i in range(minlen):
r = operate(List1[i], List2[i], fillCondition, fixedFillValue)
left.append(r[0]); right.append(r[1])
# Extra items from List1
for i in range(minlen, len(List1)):
if fillCondition == 1: match = List1[i]
elif fillCondition == 2: match = fixedFillValue
elif fillCondition == 3: match = lastItemL2
else: match = None
r = operate(List1[i], match, fillCondition, fixedFillValue)
left.append(r[0]); right.append(r[1])
# Extra items from List2
for i in range(minlen, len(List2)):
if fillCondition == 1: match = List2[i]
elif fillCondition == 2: match = fixedFillValue
elif fillCondition == 3: match = lastItemL1
else: match = None
r = operate(match, List2[i], fillCondition, fixedFillValue)
left.append(r[0]); right.append(r[1])
return [left, right]
# --- Dynamo inputs ---
# IN[0] = List1, IN[1] = List2
# IN[2] = FillGapsWithNone (bool)
# IN[3] = FillGapsWithNextListItems (bool)
# IN[4] = FillGapsWithFixedValue (bool)
# IN[5] = FillGapsWithLastItem (bool)
# IN[6] = FixedValue
fillc = 0
if IN[2]: fillc = 0
if IN[3]: fillc = 1
if IN[4]: fillc = 2
if IN[5]: fillc = 3
OUT = operate(IN[0], IN[1], fillc, IN[6]), log