Source code for static_frame.core.series_mapping

from __future__ import annotations

from collections.abc import ItemsView, Iterator, KeysView, Mapping, ValuesView

import numpy as np
import typing_extensions as tp

from static_frame.core.container_util import is_element

if tp.TYPE_CHECKING:
    from static_frame.core.generic_aliases import TIndexAny
    from static_frame.core.series import Series

TVKeys = tp.TypeVar('TVKeys', bound=np.generic)
TVValues = tp.TypeVar('TVValues', bound=np.generic)


# -------------------------------------------------------------------------------
class SeriesMappingKeysView(KeysView[TVKeys]):
    def __init__(self, series: Series):
        KeysView.__init__(self, series.index)  # pyright: ignore

    def __reversed__(self) -> Iterator[TVKeys]:
        return reversed(self._mapping)  # type: ignore


class SeriesMappingItemsView(ItemsView[TVKeys, TVValues]):
    def __init__(self, series: Series[TIndexAny, TVValues]):
        ItemsView.__init__(self, series)  # pyright: ignore

    def __reversed__(self) -> Iterator[tp.Tuple[TVKeys, TVValues]]:
        series = self._mapping  # type: ignore
        return zip(reversed(series._index), reversed(series.values))


class SeriesMappingValuesView(ValuesView[TVValues]):
    def __init__(self, series: Series[TIndexAny, TVValues]):
        self._values = series.values
        ValuesView.__init__(self, series.values)  # pyright: ignore

    def __contains__(
        self,
        key: object,
        /,
    ) -> bool:
        # linear time unavoidable
        return self._values.__contains__(key)

    def __iter__(self) -> Iterator[TVValues]:
        # ValueView base class wants to lookup keys to get values; this is more efficient.
        return iter(self._values)

    def __reversed__(self) -> Iterator[TVValues]:
        return reversed(self._values)


# -------------------------------------------------------------------------------
class SeriesMapping(Mapping[TVKeys, TVValues]):
    """A `collections.abc.Mapping` subclass that provides a view into the index and values of a `Series` as a compliant mapping type. This container is designed to be completely compatible with read-only `dict` and related interfaces. It does not copy underlying data and is immutable."""

    __slots__ = ('_series',)

    _INTERFACE = (
        '__getitem__',
        '__iter__',
        '__reversed__',
        '__len__',
        '__contains__',
        '__repr__',
        'keys',
        'values',
        'items',
    )

    def __init__(self, series: Series[TIndexAny, TVValues]):
        from static_frame.core.series import Series

        assert isinstance(series, Series)
        self._series = series

[docs] def __getitem__(self, key: TVKeys) -> TVValues: # enforce that key must be an element if key.__class__ is slice or not is_element(key): # type: ignore [comparison-overlap] raise KeyError(str(key)) return self._series._extract_loc(key) # type: ignore [no-any-return]
[docs] def __iter__(self) -> Iterator[TVKeys]: # for IndexHierarchy, these will be tuples return iter(self._series._index) # pyright: ignore
[docs] def __reversed__(self) -> Iterator[TVKeys]: return reversed(self._series._index) # pyright: ignore
[docs] def __len__(self) -> int: return len(self._series)
[docs] def __contains__( self, key: object, /, ) -> bool: return key in self._series.index
[docs] def __repr__(self) -> str: return '{}({{{}}})'.format( self.__class__.__name__, ', '.join(f'{k}: {v}' for k, v in self._series.items()), )
# ---------------------------------------------------------------------------
[docs] def keys(self) -> SeriesMappingKeysView[TVKeys]: return SeriesMappingKeysView(self._series)
[docs] def values(self) -> SeriesMappingValuesView[TVValues]: return SeriesMappingValuesView(self._series)
[docs] def items(self) -> SeriesMappingItemsView[TVKeys, TVValues]: return SeriesMappingItemsView(self._series)