Skip to content

zarr_indexing.output_map

zarr_indexing.output_map

Output index maps — three ordered mappings to integer coordinates.

An output index map describes how input cells address one dimension of the output space. Its coordinates form an ordered, duplicate-preserving sequence aligned with the input domain, never a mathematical set. Three representations cover the cases that arise in practice:

  • ConstantMap(offset=5) — every request cell maps to coordinate 5
  • DimensionMap(input_dimension=0, offset=3, stride=2) over input [0, 5) — the ordered arithmetic progression [3, 5, 7, 9, 11]
  • ArrayMap(index_array=[5, 1, 1]) — the explicit sequence [5, 1, 1], preserving both order and the repeated coordinate

Every output map participates in two operations defined on IndexTransform, which provides the input-domain context these maps lack:

  • intersect — retain mapped cells whose coordinates lie within a range (e.g., a chunk), without changing their order or multiplicity. Restricting [3, 5, 5, 9] to [4, 8) produces [5, 5].
  • translate — shift every coordinate by a constant (e.g., make chunk-local). Translating [5, 5, 7] by -4 produces [1, 1, 3].

These two operations are the foundation of chunk resolution: for each chunk, intersect the map with the chunk's range, then translate to chunk-local coordinates.

The three types exist because they trade off generality for efficiency:

  • ConstantMap: O(1) storage, O(1) intersection
  • DimensionMap: O(1) storage, O(1) intersection (analytical)
  • ArrayMap: O(n) storage, O(n) intersection (must scan the array)

Collapsing everything to ArrayMap would be correct but wasteful — a billion-element slice would materialize a billion coordinates just to group them by chunk, when DimensionMap does it with three integers.

OutputIndexMap module-attribute

OutputIndexMap = ConstantMap | DimensionMap | ArrayMap

ArrayMap dataclass

An explicit ordered, duplicate-preserving coordinate mapping.

Maps each input position i to offset + stride * index_array[i]. Index-array order and repeated entries are semantic and remain present in the result. Arises from fancy indexing (e.g., arr[[5, 1, 1]] or boolean masks).

Freshly constructed maps are normalized to the full input rank of their enclosing transform: index_array has the enclosing domain's rank, sized fully on the axes it varies over and singleton (size 1) elsewhere. The shape is the single source of truth for what the map depends on — its dependency axes are exactly its non-singleton axes (see _array_map_dependency_axes) — and it distinguishes the two flavors of multi-array fancy indexing:

  • orthogonal (oindex): each array varies along a single, distinct axis (all others singleton); the result is their outer product.
  • vectorized (vindex): the arrays are correlated and share the same non-singleton (broadcast) axes; the result is a pointwise scatter.

A map holding exactly one coordinate carries no shape to read a dependency from, and none is needed: it is the ConstantMap it equals, and the selection layer builds that instead (see array_map_or_constant). A hand-built all-singleton ArrayMap is still a valid value; resolution classifies it with the correlated maps and reads it pointwise.

Examples:

The fancy selection arr[[5, 1, 1]] reads coordinate 5, then 1, then 1 — order and the duplicate preserved, exactly as NumPy fancy indexing:

>>> m = ArrayMap(index_array=np.array([5, 1, 1]))
>>> [m.offset + m.stride * c for c in m.index_array.tolist()]
[5, 1, 1]
>>> np.arange(10)[[5, 1, 1]].tolist()
[5, 1, 1]
Source code in src/zarr_indexing/output_map.py
@dataclass(frozen=True, slots=True)
class ArrayMap:
    """An explicit ordered, duplicate-preserving coordinate mapping.

    Maps each input position `i` to `offset + stride * index_array[i]`.
    Index-array order and repeated entries are semantic and remain present in
    the result. Arises from fancy indexing (e.g., `arr[[5, 1, 1]]` or boolean
    masks).

    Freshly constructed maps are normalized to the **full input rank** of their
    enclosing transform: `index_array` has the enclosing domain's rank, sized
    fully on the axes it varies over and singleton (size 1) elsewhere. The
    shape is the single source of truth for what the map depends on — its
    **dependency axes** are exactly its non-singleton axes (see
    `_array_map_dependency_axes`) — and it distinguishes the two
    flavors of multi-array fancy indexing:

    - **orthogonal** (`oindex`): each array varies along a single, *distinct*
      axis (all others singleton); the result is their outer product.
    - **vectorized** (`vindex`): the arrays are correlated and share the same
      non-singleton (broadcast) axes; the result is a pointwise scatter.

    A map holding exactly one coordinate carries no shape to read a dependency
    from, and none is needed: it is the `ConstantMap` it equals, and the
    selection layer builds that instead (see `array_map_or_constant`). A
    hand-built all-singleton `ArrayMap` is still a valid value; resolution
    classifies it with the correlated maps and reads it pointwise.

    Examples
    --------
    The fancy selection `arr[[5, 1, 1]]` reads coordinate 5, then 1, then 1
    — order and the duplicate preserved, exactly as NumPy fancy indexing:

    >>> m = ArrayMap(index_array=np.array([5, 1, 1]))
    >>> [m.offset + m.stride * c for c in m.index_array.tolist()]
    [5, 1, 1]
    >>> np.arange(10)[[5, 1, 1]].tolist()
    [5, 1, 1]
    """

    index_array: npt.NDArray[np.integer[Any]]
    """Explicit coordinates at the enclosing transform's full input rank; order and
    duplicates are semantic. Its non-singleton axes are the map's dependency axes."""

    offset: int = 0
    """Constant term of the affine adjustment: the output coordinate is `offset + stride * index_array[i]`."""

    stride: int = 1
    """Multiplier applied to each `index_array` value before `offset` is added."""

    def __post_init__(self) -> None:
        """Own the index array and expose it read-only.

        A map is frozen, but the array inside it was not: reaching through a
        view's transform to `index_array[0] = 9` silently changed what the view
        returned, in a package whose whole contract is that a view is a
        description of a read and resolving it twice answers alike. Owning the
        array also prevents the caller from changing the contents behind the
        read-only view, which would invalidate this value object's hash.
        """
        # Immutable bytes are the ultimate owner so callers cannot re-enable
        # the WRITEABLE flag, as they can on a read-only array that owns its
        # allocation. `asarray` also accepts the NumPy scalars that reach here
        # after indexing an array down to one element.
        array = np.asarray(self.index_array)
        if not np.issubdtype(array.dtype, np.integer):
            raise TypeError(f"index_array must have an integer dtype, got {array.dtype}")
        normalized = checked_affine(0, 1, array)
        frozen = np.frombuffer(normalized.tobytes(), dtype=np.intp).reshape(normalized.shape)
        object.__setattr__(self, "index_array", frozen)

    def __reduce__(self) -> tuple[object, tuple[object, int, int]]:
        """Reconstruct through `__init__`, preserving the ownership invariant."""
        return (
            type(self),
            (self.index_array, self.offset, self.stride),
        )

    def _with_affine(self, offset: int, stride: int) -> ArrayMap:
        """This map's coordinates under a different affine adjustment.

        The frozen index array is shared rather than copied: it is already
        owned by immutable bytes and read-only, so the ownership invariant
        `__post_init__` establishes holds for the new map too. Chunk
        resolution translates every restricted map once per chunk, and
        re-copying the array there dominated the cost of small selections.
        """
        new = object.__new__(ArrayMap)
        object.__setattr__(new, "index_array", self.index_array)
        object.__setattr__(new, "offset", offset)
        object.__setattr__(new, "stride", stride)
        return new

    def __eq__(self, other: object) -> bool:
        """Value equality, comparing index arrays element-wise.

        The generated `__eq__` compares them with `==`, whose result for two
        arrays is an array — so asking whether two maps are equal raised
        `ValueError: the truth value of an array ... is ambiguous`. `frozen=True`
        reads as a promise that a value can be compared and hashed, and this is
        what makes good on it.
        """
        if not isinstance(other, ArrayMap):
            return NotImplemented
        return (
            self.offset == other.offset
            and self.stride == other.stride
            and self.index_array.shape == other.index_array.shape
            and bool(np.array_equal(self.index_array, other.index_array))
        )

    def __hash__(self) -> int:
        """Hashed by the array's contents, so equal maps hash alike.

        The generated `__hash__` hashed the ndarray itself, which is unhashable;
        a map could therefore not go in a set, or key a cache.
        """
        return hash(
            (
                self.offset,
                self.stride,
                self.index_array.shape,
                self.index_array.tobytes(),
            )
        )

    @property
    def dependency_axes(self) -> tuple[int, ...]:
        """Every input axis this map varies over: its non-singleton axes.

        One axis means orthogonal, several mean correlated, and none means
        the map is degenerate — the shape is the single source of truth for
        all three.

        Examples
        --------
        >>> ArrayMap(index_array=np.array([[4, 0, 2]])).dependency_axes
        (1,)
        >>> ArrayMap(index_array=np.array([[1, 2], [3, 4]])).dependency_axes
        (0, 1)
        """
        return _array_map_dependency_axes(self.index_array)

    @property
    def dependent_axis(self) -> int | None:
        """Return the single input axis an orthogonal `ArrayMap` varies over.

        This is the array's one non-singleton axis, read from the shape — the
        single source of truth for what a map depends on. The selection layer
        collapses a single-coordinate map to a `ConstantMap`
        (`array_map_or_constant`), so a non-empty map built by this package always
        has at least one dependency axis.

        Returns
        -------
        int or None
            The axis the map varies over, or `None` when it varies over no input
            axis at all — an empty map, or a hand-built all-singleton one. `None`
            is a valid result, not an error; such maps resolve through the
            pointwise (general) path.

        Raises
        ------
        ValueError
            If the map varies over more than one axis, which makes it correlated
            rather than orthogonal.

        Examples
        --------
        An `oindex` selection on axis 1 of a rank-2 transform stores its
        coordinates full-sized on axis 1 and singleton on axis 0, so the
        dependency axis is read straight off the shape:

        >>> m = ArrayMap(index_array=np.array([[4, 0, 2]]))
        >>> m.index_array.shape
        (1, 3)
        >>> m.dependent_axis
        1
        """
        dep = self.dependency_axes
        if len(dep) == 1:
            return dep[0]
        if len(dep) == 0:
            return None
        raise ValueError(
            f"orthogonal ArrayMap must vary over exactly one axis; got dependency axes {dep}"
        )

    def to_json(self) -> OutputIndexMapJSON:
        """Convert to the canonical wire form, collapsing a degenerate map.

        A map holding exactly one coordinate, or none at all, is emitted as a
        `constant` map — see the module note on the wire format in
        [`zarr_indexing.json`][zarr_indexing.json]. Both are degenerate: the
        first selects one coordinate whatever the input, and the second names
        no cell and can only be empty because an input dimension is, so the
        emptiness travels in the domain instead.

        Examples
        --------
        >>> ArrayMap(np.array([[4], [1], [1]])).to_json()["index_array"]
        [[4], [1], [1]]
        >>> ArrayMap(np.array([7])).to_json()  # degenerate: one coordinate
        {'offset': 7}
        """
        if self.index_array.size == 1:
            value = int(self.index_array.reshape(-1)[0])
            return {"offset": self.offset + self.stride * value}
        if self.index_array.size == 0:
            return {"offset": 0}
        return {
            "offset": self.offset,
            "stride": self.stride,
            "index_array": self.index_array.tolist(),
            "index_array_bounds": ["-inf", "+inf"],
        }

dependency_axes property

dependency_axes: tuple[int, ...]

Every input axis this map varies over: its non-singleton axes.

One axis means orthogonal, several mean correlated, and none means the map is degenerate — the shape is the single source of truth for all three.

Examples:

>>> ArrayMap(index_array=np.array([[4, 0, 2]])).dependency_axes
(1,)
>>> ArrayMap(index_array=np.array([[1, 2], [3, 4]])).dependency_axes
(0, 1)

dependent_axis property

dependent_axis: int | None

Return the single input axis an orthogonal ArrayMap varies over.

This is the array's one non-singleton axis, read from the shape — the single source of truth for what a map depends on. The selection layer collapses a single-coordinate map to a ConstantMap (array_map_or_constant), so a non-empty map built by this package always has at least one dependency axis.

Returns:

  • int or None

    The axis the map varies over, or None when it varies over no input axis at all — an empty map, or a hand-built all-singleton one. None is a valid result, not an error; such maps resolve through the pointwise (general) path.

Raises:

  • ValueError

    If the map varies over more than one axis, which makes it correlated rather than orthogonal.

Examples:

An oindex selection on axis 1 of a rank-2 transform stores its coordinates full-sized on axis 1 and singleton on axis 0, so the dependency axis is read straight off the shape:

>>> m = ArrayMap(index_array=np.array([[4, 0, 2]]))
>>> m.index_array.shape
(1, 3)
>>> m.dependent_axis
1

index_array instance-attribute

index_array: NDArray[integer[Any]]

Explicit coordinates at the enclosing transform's full input rank; order and duplicates are semantic. Its non-singleton axes are the map's dependency axes.

offset class-attribute instance-attribute

offset: int = 0

Constant term of the affine adjustment: the output coordinate is offset + stride * index_array[i].

stride class-attribute instance-attribute

stride: int = 1

Multiplier applied to each index_array value before offset is added.

__eq__

__eq__(other: object) -> bool

Value equality, comparing index arrays element-wise.

The generated __eq__ compares them with ==, whose result for two arrays is an array — so asking whether two maps are equal raised ValueError: the truth value of an array ... is ambiguous. frozen=True reads as a promise that a value can be compared and hashed, and this is what makes good on it.

Source code in src/zarr_indexing/output_map.py
def __eq__(self, other: object) -> bool:
    """Value equality, comparing index arrays element-wise.

    The generated `__eq__` compares them with `==`, whose result for two
    arrays is an array — so asking whether two maps are equal raised
    `ValueError: the truth value of an array ... is ambiguous`. `frozen=True`
    reads as a promise that a value can be compared and hashed, and this is
    what makes good on it.
    """
    if not isinstance(other, ArrayMap):
        return NotImplemented
    return (
        self.offset == other.offset
        and self.stride == other.stride
        and self.index_array.shape == other.index_array.shape
        and bool(np.array_equal(self.index_array, other.index_array))
    )

__hash__

__hash__() -> int

Hashed by the array's contents, so equal maps hash alike.

The generated __hash__ hashed the ndarray itself, which is unhashable; a map could therefore not go in a set, or key a cache.

Source code in src/zarr_indexing/output_map.py
def __hash__(self) -> int:
    """Hashed by the array's contents, so equal maps hash alike.

    The generated `__hash__` hashed the ndarray itself, which is unhashable;
    a map could therefore not go in a set, or key a cache.
    """
    return hash(
        (
            self.offset,
            self.stride,
            self.index_array.shape,
            self.index_array.tobytes(),
        )
    )

__init__

__init__(
    index_array: NDArray[integer[Any]],
    offset: int = 0,
    stride: int = 1,
) -> None

__post_init__

__post_init__() -> None

Own the index array and expose it read-only.

A map is frozen, but the array inside it was not: reaching through a view's transform to index_array[0] = 9 silently changed what the view returned, in a package whose whole contract is that a view is a description of a read and resolving it twice answers alike. Owning the array also prevents the caller from changing the contents behind the read-only view, which would invalidate this value object's hash.

Source code in src/zarr_indexing/output_map.py
def __post_init__(self) -> None:
    """Own the index array and expose it read-only.

    A map is frozen, but the array inside it was not: reaching through a
    view's transform to `index_array[0] = 9` silently changed what the view
    returned, in a package whose whole contract is that a view is a
    description of a read and resolving it twice answers alike. Owning the
    array also prevents the caller from changing the contents behind the
    read-only view, which would invalidate this value object's hash.
    """
    # Immutable bytes are the ultimate owner so callers cannot re-enable
    # the WRITEABLE flag, as they can on a read-only array that owns its
    # allocation. `asarray` also accepts the NumPy scalars that reach here
    # after indexing an array down to one element.
    array = np.asarray(self.index_array)
    if not np.issubdtype(array.dtype, np.integer):
        raise TypeError(f"index_array must have an integer dtype, got {array.dtype}")
    normalized = checked_affine(0, 1, array)
    frozen = np.frombuffer(normalized.tobytes(), dtype=np.intp).reshape(normalized.shape)
    object.__setattr__(self, "index_array", frozen)

__reduce__

__reduce__() -> tuple[object, tuple[object, int, int]]

Reconstruct through __init__, preserving the ownership invariant.

Source code in src/zarr_indexing/output_map.py
def __reduce__(self) -> tuple[object, tuple[object, int, int]]:
    """Reconstruct through `__init__`, preserving the ownership invariant."""
    return (
        type(self),
        (self.index_array, self.offset, self.stride),
    )

to_json

to_json() -> OutputIndexMapJSON

Convert to the canonical wire form, collapsing a degenerate map.

A map holding exactly one coordinate, or none at all, is emitted as a constant map — see the module note on the wire format in zarr_indexing.json. Both are degenerate: the first selects one coordinate whatever the input, and the second names no cell and can only be empty because an input dimension is, so the emptiness travels in the domain instead.

Examples:

>>> ArrayMap(np.array([[4], [1], [1]])).to_json()["index_array"]
[[4], [1], [1]]
>>> ArrayMap(np.array([7])).to_json()  # degenerate: one coordinate
{'offset': 7}
Source code in src/zarr_indexing/output_map.py
def to_json(self) -> OutputIndexMapJSON:
    """Convert to the canonical wire form, collapsing a degenerate map.

    A map holding exactly one coordinate, or none at all, is emitted as a
    `constant` map — see the module note on the wire format in
    [`zarr_indexing.json`][zarr_indexing.json]. Both are degenerate: the
    first selects one coordinate whatever the input, and the second names
    no cell and can only be empty because an input dimension is, so the
    emptiness travels in the domain instead.

    Examples
    --------
    >>> ArrayMap(np.array([[4], [1], [1]])).to_json()["index_array"]
    [[4], [1], [1]]
    >>> ArrayMap(np.array([7])).to_json()  # degenerate: one coordinate
    {'offset': 7}
    """
    if self.index_array.size == 1:
        value = int(self.index_array.reshape(-1)[0])
        return {"offset": self.offset + self.stride * value}
    if self.index_array.size == 0:
        return {"offset": 0}
    return {
        "offset": self.offset,
        "stride": self.stride,
        "index_array": self.index_array.tolist(),
        "index_array_bounds": ["-inf", "+inf"],
    }

ConstantMap dataclass

A constant output-coordinate mapping.

Every input cell maps to offset. Arises from integer indexing (e.g., arr[5] fixes one dimension to coordinate 5).

Examples:

Every input cell maps to the same output coordinate — the NumPy analogy is a broadcast (np.broadcast_to(5, (3,))), not an index:

>>> from zarr_indexing.domain import IndexDomain
>>> from zarr_indexing.transform import IndexTransform
>>> domain = IndexDomain.from_shape((3,))
>>> t = IndexTransform(domain=domain, output=(ConstantMap(offset=5),))
>>> t.apply((0,)), t.apply((1,)), t.apply((2,))
((5,), (5,), (5,))
Source code in src/zarr_indexing/output_map.py
@dataclass(frozen=True, slots=True)
class ConstantMap:
    """A constant output-coordinate mapping.

    Every input cell maps to `offset`. Arises from integer indexing (e.g.,
    `arr[5]` fixes one dimension to coordinate 5).

    Examples
    --------
    Every input cell maps to the same output coordinate — the NumPy analogy
    is a broadcast (`np.broadcast_to(5, (3,))`), not an index:

    >>> from zarr_indexing.domain import IndexDomain
    >>> from zarr_indexing.transform import IndexTransform
    >>> domain = IndexDomain.from_shape((3,))
    >>> t = IndexTransform(domain=domain, output=(ConstantMap(offset=5),))
    >>> t.apply((0,)), t.apply((1,)), t.apply((2,))
    ((5,), (5,), (5,))
    """

    offset: int = 0
    """The fixed output coordinate every input cell maps to."""

    def to_json(self) -> OutputIndexMapJSON:
        """Convert to the canonical wire form: the bare `constant` map.

        Examples
        --------
        >>> ConstantMap(5).to_json()
        {'offset': 5}
        """
        return {"offset": self.offset}

offset class-attribute instance-attribute

offset: int = 0

The fixed output coordinate every input cell maps to.

__init__

__init__(offset: int = 0) -> None

to_json

to_json() -> OutputIndexMapJSON

Convert to the canonical wire form: the bare constant map.

Examples:

>>> ConstantMap(5).to_json()
{'offset': 5}
Source code in src/zarr_indexing/output_map.py
def to_json(self) -> OutputIndexMapJSON:
    """Convert to the canonical wire form: the bare `constant` map.

    Examples
    --------
    >>> ConstantMap(5).to_json()
    {'offset': 5}
    """
    return {"offset": self.offset}

DimensionMap dataclass

An ordered affine mapping to output coordinates.

Maps each input coordinate i to offset + stride * i, where the input range comes from the enclosing IndexTransform's domain. Arises from slice indexing (e.g., arr[2:10:3] gives offset=2, stride=3).

Examples:

The slice arr[2:11:3] reads coordinates 2, 5, 8 — the rule offset + stride * i with offset=2, stride=3:

>>> m = DimensionMap(input_dimension=0, offset=2, stride=3)
>>> [m.offset + m.stride * i for i in range(3)]
[2, 5, 8]
>>> np.arange(11)[2:11:3].tolist()
[2, 5, 8]
Source code in src/zarr_indexing/output_map.py
@dataclass(frozen=True, slots=True)
class DimensionMap:
    """An ordered affine mapping to output coordinates.

    Maps each input coordinate `i` to `offset + stride * i`, where the input
    range comes from the enclosing `IndexTransform`'s domain. Arises from slice
    indexing (e.g., `arr[2:10:3]` gives offset=2, stride=3).

    Examples
    --------
    The slice `arr[2:11:3]` reads coordinates `2, 5, 8` — the rule
    `offset + stride * i` with `offset=2`, `stride=3`:

    >>> m = DimensionMap(input_dimension=0, offset=2, stride=3)
    >>> [m.offset + m.stride * i for i in range(3)]
    [2, 5, 8]
    >>> np.arange(11)[2:11:3].tolist()
    [2, 5, 8]
    """

    input_dimension: int
    """The input (domain) dimension whose coordinate this map reads."""

    offset: int = 0
    """The output coordinate that input coordinate `0` maps to."""

    stride: int = 1
    """The output-coordinate step per unit input step; negative walks backward, zero repeats `offset`."""

    def to_json(self) -> OutputIndexMapJSON:
        """Convert to the canonical wire form: the `single_input_dimension` map.

        Examples
        --------
        >>> DimensionMap(input_dimension=1, offset=0, stride=2).to_json()
        {'offset': 0, 'stride': 2, 'input_dimension': 1}
        """
        return {
            "offset": self.offset,
            "stride": self.stride,
            "input_dimension": self.input_dimension,
        }

input_dimension instance-attribute

input_dimension: int

The input (domain) dimension whose coordinate this map reads.

offset class-attribute instance-attribute

offset: int = 0

The output coordinate that input coordinate 0 maps to.

stride class-attribute instance-attribute

stride: int = 1

The output-coordinate step per unit input step; negative walks backward, zero repeats offset.

__init__

__init__(
    input_dimension: int, offset: int = 0, stride: int = 1
) -> None

to_json

to_json() -> OutputIndexMapJSON

Convert to the canonical wire form: the single_input_dimension map.

Examples:

>>> DimensionMap(input_dimension=1, offset=0, stride=2).to_json()
{'offset': 0, 'stride': 2, 'input_dimension': 1}
Source code in src/zarr_indexing/output_map.py
def to_json(self) -> OutputIndexMapJSON:
    """Convert to the canonical wire form: the `single_input_dimension` map.

    Examples
    --------
    >>> DimensionMap(input_dimension=1, offset=0, stride=2).to_json()
    {'offset': 0, 'stride': 2, 'input_dimension': 1}
    """
    return {
        "offset": self.offset,
        "stride": self.stride,
        "input_dimension": self.input_dimension,
    }

array_map_or_constant

array_map_or_constant(
    index_array: NDArray[integer[Any]],
    offset: int = 0,
    stride: int = 1,
) -> ArrayMap | ConstantMap

An ArrayMap, collapsed to the ConstantMap it equals when it can be.

An index array holding exactly one coordinate maps every input cell to the same place; representing it as a lookup table would leave a map whose shape names no dependency axis, the one form the shape-derived classifier cannot read. The selection and composition layers build their array maps through this helper so that a non-empty ArrayMap always varies over at least one axis. An empty array stays an ArrayMap: it maps no cell at all, and the emptiness lives in the domain that accompanies it.

Source code in src/zarr_indexing/output_map.py
def array_map_or_constant(
    index_array: npt.NDArray[np.integer[Any]],
    offset: int = 0,
    stride: int = 1,
) -> ArrayMap | ConstantMap:
    """An `ArrayMap`, collapsed to the `ConstantMap` it equals when it can be.

    An index array holding exactly one coordinate maps every input cell to the
    same place; representing it as a lookup table would leave a map whose shape
    names no dependency axis, the one form the shape-derived classifier cannot
    read. The selection and composition layers build their array maps through
    this helper so that a non-empty `ArrayMap` always varies over at least one
    axis. An empty array stays an `ArrayMap`: it maps no cell at all, and the
    emptiness lives in the domain that accompanies it.
    """
    arr = np.asarray(index_array)
    if arr.size == 1:
        return ConstantMap(offset=checked_affine(offset, stride, int(arr.reshape(-1)[0])))
    return ArrayMap(index_array=arr, offset=offset, stride=stride)

output_index_map_from_json

output_index_map_from_json(
    data: OutputIndexMapJSON,
) -> OutputIndexMap

Construct the output map a canonical wire form names.

The wire form is a tagged union — index_array, then input_dimension, else constant — so loading it dispatches to the right kind here rather than on any one of them.

Examples:

>>> output_index_map_from_json({"offset": 5})
ConstantMap(offset=5)
>>> output_index_map_from_json({"offset": 0, "stride": 2, "input_dimension": 1})
DimensionMap(input_dimension=1, offset=0, stride=2)
Source code in src/zarr_indexing/output_map.py
def output_index_map_from_json(data: OutputIndexMapJSON) -> OutputIndexMap:
    """Construct the output map a canonical wire form names.

    The wire form is a tagged union — `index_array`, then `input_dimension`,
    else constant — so loading it dispatches to the right kind here rather
    than on any one of them.

    Examples
    --------
    >>> output_index_map_from_json({"offset": 5})
    ConstantMap(offset=5)
    >>> output_index_map_from_json({"offset": 0, "stride": 2, "input_dimension": 1})
    DimensionMap(input_dimension=1, offset=0, stride=2)
    """
    from zarr_indexing._wire import lower_index_array

    if "index_array" in data:
        return ArrayMap(
            index_array=lower_index_array(data["index_array"], "index_array"),
            offset=data.get("offset", 0),
            stride=data.get("stride", 1),
        )
    if "input_dimension" in data:
        return DimensionMap(
            input_dimension=data["input_dimension"],
            offset=data.get("offset", 0),
            stride=data.get("stride", 1),
        )
    return ConstantMap(offset=data.get("offset", 0))