Dynamo Lists - Levelize List — Digitteck
Dynamo Lists - Levelize List
dotnet·29 May 2018·4 min read

Dynamo Lists - Levelize List

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

Tags

DynamoPythonListsComputational Design
digitteck

© 2026 Digitteck