Skip to content

Collection

collection

Generic array helpers used internally by other namespaces.

The Collection namespace exposes the small set of utility functions that other tonal modules rely on. They have no music-theory content — just range, rotate, compact, shuffle, permutations.

Example

from tonal_py import Collection Collection.range(2, 5) [2, 3, 4, 5] Collection.rotate(2, [1, 2, 3, 4, 5]) [3, 4, 5, 1, 2] Collection.compact([0, None, 1, "", 2]) [0, 1, 2]

Source parity: @tonaljs/collection

range

range(from_: int, to: int) -> list[int]

Inclusive integer range, ascending or descending automatically.

Parameters:

Name Type Description Default
from_ int

Start of range (inclusive).

required
to int

End of range (inclusive).

required

Returns:

Type Description
list[int]

Integers from from_ to to (direction depends on whether

list[int]

from_ < to).

Example

from tonal_py import Collection Collection.range(2, 5) [2, 3, 4, 5] Collection.range(5, 2) [5, 4, 3, 2] Collection.range(3, 3) [3]

Source code in src/tonal_py/collection.py
def range(from_: int, to: int) -> list[int]:
    """Inclusive integer range, ascending or descending automatically.

    Args:
        from_: Start of range (inclusive).
        to: End of range (inclusive).

    Returns:
        Integers from `from_` to `to` (direction depends on whether
        `from_ < to`).

    Example:
        >>> from tonal_py import Collection
        >>> Collection.range(2, 5)
        [2, 3, 4, 5]
        >>> Collection.range(5, 2)
        [5, 4, 3, 2]
        >>> Collection.range(3, 3)
        [3]
    """
    if from_ < to:
        return [from_ + i for i in builtins.range(to - from_ + 1)]
    return [from_ - i for i in builtins.range(from_ - to + 1)]

rotate

rotate(times: int, arr: list[T]) -> list[T]

Rotate arr left by times positions (negative rotates right).

Parameters:

Name Type Description Default
times int

Rotation count.

required
arr list[T]

List to rotate.

required

Returns:

Type Description
list[T]

New rotated list (input unchanged).

Example

from tonal_py import Collection Collection.rotate(2, [1, 2, 3, 4, 5]) [3, 4, 5, 1, 2] Collection.rotate(-1, [1, 2, 3, 4, 5]) [5, 1, 2, 3, 4] Collection.rotate(7, [1, 2, 3, 4, 5]) # wraps via modulo [3, 4, 5, 1, 2]

Source code in src/tonal_py/collection.py
def rotate(times: int, arr: list[T]) -> list[T]:
    """Rotate `arr` left by `times` positions (negative rotates right).

    Args:
        times: Rotation count.
        arr: List to rotate.

    Returns:
        New rotated list (input unchanged).

    Example:
        >>> from tonal_py import Collection
        >>> Collection.rotate(2, [1, 2, 3, 4, 5])
        [3, 4, 5, 1, 2]
        >>> Collection.rotate(-1, [1, 2, 3, 4, 5])
        [5, 1, 2, 3, 4]
        >>> Collection.rotate(7, [1, 2, 3, 4, 5])      # wraps via modulo
        [3, 4, 5, 1, 2]
    """
    length = len(arr)
    if length == 0:
        return []
    n = (times % length + length) % length
    return arr[n:] + arr[:n]

compact

compact(arr: list[T]) -> list[T]

Remove JS-falsy values from arr, keeping 0.

Mirrors JS arr.filter(n => n === 0 || n). Strips None, False, "", and NaN. Keeps integer 0 (and 0.0).

Parameters:

Name Type Description Default
arr list[T]

A list of mixed values.

required

Returns:

Type Description
list[T]

New list with falsy values removed (but 0 preserved).

Example

from tonal_py import Collection Collection.compact([0, 1, None, "", "a", 2]) [0, 1, 'a', 2] Collection.compact([0, False, 0.0]) # bool False stripped, int 0 kept [0, 0.0]

Source code in src/tonal_py/collection.py
def compact(arr: list[T]) -> list[T]:
    """Remove JS-falsy values from `arr`, keeping `0`.

    Mirrors JS `arr.filter(n => n === 0 || n)`. Strips `None`, `False`,
    `""`, and `NaN`. Keeps integer `0` (and `0.0`).

    Args:
        arr: A list of mixed values.

    Returns:
        New list with falsy values removed (but `0` preserved).

    Example:
        >>> from tonal_py import Collection
        >>> Collection.compact([0, 1, None, "", "a", 2])
        [0, 1, 'a', 2]
        >>> Collection.compact([0, False, 0.0])     # bool False stripped, int 0 kept
        [0, 0.0]
    """
    out: list[T] = []
    for n in arr:
        if isinstance(n, bool):
            if n:
                out.append(n)
            continue
        if n == 0:
            out.append(n)
            continue
        if isinstance(n, float) and n != n:  # NaN
            continue
        if n:
            out.append(n)
    return out

shuffle

shuffle(arr: list[T], rnd: Callable[[], float] | None = None) -> list[T]

In-place Fisher-Yates shuffle.

Mutates arr (matches JS) and returns the same list reference.

Parameters:

Name Type Description Default
arr list[T]

List to shuffle (mutated in place).

required
rnd Callable[[], float] | None

Optional RNG. Defaults to random.random. Inject a deterministic generator for tests.

None

Returns:

Type Description
list[T]

The same arr, now shuffled.

Example

from tonal_py import Collection arr = [1, 2, 3] shuffled = Collection.shuffle(arr) sorted(shuffled) == [1, 2, 3] True shuffled is arr # mutated in place True

Source code in src/tonal_py/collection.py
def shuffle(arr: list[T], rnd: Callable[[], float] | None = None) -> list[T]:
    """In-place Fisher-Yates shuffle.

    Mutates `arr` (matches JS) and returns the same list reference.

    Args:
        arr: List to shuffle (mutated in place).
        rnd: Optional RNG. Defaults to `random.random`. Inject a
            deterministic generator for tests.

    Returns:
        The same `arr`, now shuffled.

    Example:
        >>> from tonal_py import Collection
        >>> arr = [1, 2, 3]
        >>> shuffled = Collection.shuffle(arr)
        >>> sorted(shuffled) == [1, 2, 3]
        True
        >>> shuffled is arr            # mutated in place
        True
    """
    rng = rnd if rnd is not None else _random.random
    m = len(arr)
    while m:
        i = int(rng() * m)
        m -= 1
        arr[m], arr[i] = arr[i], arr[m]
    return arr

permutations

permutations(arr: list[T]) -> list[list[T]]

Generate all permutations of arr.

O(n!) — be aware before calling on large lists.

Parameters:

Name Type Description Default
arr list[T]

A list.

required

Returns:

Type Description
list[list[T]]

Every permutation as a list of lists. Order matches tonal.js

list[list[T]]

(recursive insertion).

Example

from tonal_py import Collection Collection.permutations([1, 2, 3]) # doctest: +NORMALIZE_WHITESPACE [[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]

Source code in src/tonal_py/collection.py
def permutations(arr: list[T]) -> list[list[T]]:
    """Generate all permutations of `arr`.

    O(n!) — be aware before calling on large lists.

    Args:
        arr: A list.

    Returns:
        Every permutation as a list of lists. Order matches tonal.js
        (recursive insertion).

    Example:
        >>> from tonal_py import Collection
        >>> Collection.permutations([1, 2, 3])  # doctest: +NORMALIZE_WHITESPACE
        [[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]
    """
    if len(arr) == 0:
        return [[]]
    head = arr[0]
    tail_perms = permutations(arr[1:])
    out: list[list[T]] = []
    for perm in tail_perms:
        for pos in builtins.range(len(arr)):
            new_perm = perm.copy()
            new_perm.insert(pos, head)
            out.append(new_perm)
    return out