Module grscheller.datastructures.flarray
Module grscheller.datastructure.flarray - Fixed length array
Implements fixed length arrays of values of arbitrary types. O(1) data access which will store None values. The arrays must have length > 0 and are guarnteed not to change size.
Expand source code
# Copyright 2023 Geoffrey R. Scheller
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module grscheller.datastructure.flarray - Fixed length array
Implements fixed length arrays of values of arbitrary types. O(1) data access
which will store None values. The arrays must have length > 0 and are
guarnteed not to change size.
"""
from __future__ import annotations
__all__ = ['FLarray']
__author__ = "Geoffrey R. Scheller"
__copyright__ = "Copyright (c) 2023 Geoffrey R. Scheller"
__license__ = "Appache License 2.0"
from typing import Any, Callable, Never, Union
from .iterlib import mapIter, concatIters, mergeIters
class FLarray():
"""Class representing a fixed length array data structure of length > 0.
Permits storing None as a value.
"""
def __init__(self, *ds, size: int = 0, default: Any = None):
"""Construct a fixed length array
- guarnteed to be of length |size| for size != 0
- if size not indicated (or 0), size to data provided
- if no data provided, return array with default value of size = 1
- assign missing values the default value
- if size < 0, pad provided data on left or slice it on the right
"""
dlist = list(ds)
dsize = len(dlist)
match (size, abs(size) == dsize, abs(size) > dsize):
case (0, _, _):
# default to the size of the data given
if dsize > 0:
self._size = dsize
self._list = dlist
else:
# ensure flarray not empty
self._size = 1
self._list = [default]
case (_, True, _):
# no size inconsistencies
self._size = dsize
self._list = dlist
case (_, _, True):
if size > 0:
# pad higher indexes (on "right")
self._size = size
self._list = dlist + [default]*(size - dsize)
else:
# pad lower indexes (on "left")
dlist.reverse()
dlist += [default]*(-size - dsize)
dlist.reverse()
self._size = -size
self._list = dlist + [default]*(size - dsize)
case _:
if size > 0:
# take left slice, ignore extra data at end
self._size = size
self._list = dlist[0:size]
else:
# take right slice, ignore extra data at beginning
self._size = -size
self._list = dlist[dsize+size:]
def __bool__(self):
"""Returns true if not all values evaluate as False"""
for ii in range(self._size):
if self._list[ii]:
return True
return False
def __len__(self) -> int:
"""Returns the size of the flarray"""
return self._size
def __getitem__(self, index: int) -> Union[Any, Never]:
# TODO: Does not like being given a slice ... research
cnt = self._size
if not -cnt <= index < cnt:
l = -cnt
h = cnt - 1
msg = f'fdArray index = {index} not between {l} and {h} while getting value'
raise IndexError(msg)
return self._list[index]
def __setitem__(self, index: int, value: Any) -> Union[None, Never]:
cnt = self._size
if not -cnt <= index < cnt:
l = -cnt
h = cnt - 1
msg = f'fdArray index = {index} not between {l} and {h} while getting value'
raise IndexError(msg)
self._list[index] = value
def __iter__(self):
"""Iterator yielding data currently stored in flarray"""
currList = self._list.copy()
for pos in range(self._size):
yield currList[pos]
def __reversed__(self):
"""Reverse iterate over the current state of the flarray"""
for data in reversed(self._list.copy()):
yield data
def __eq__(self, other):
"""Returns True if all the data stored in both compare as equal.
Worst case is O(n) behavior for the true case.
"""
if not isinstance(other, type(self)):
return False
return self._list == other._list
def __repr__(self):
"""Display data in flarray"""
listStrs = []
for data in self:
listStrs.append(repr(data))
return "[| " + ", ".join(listStrs) + " |]"
def copy(self) -> FLarray:
"""Return shallow copy of the flarray in O(n) time & space complexity"""
return FLarray(*self)
def map(self, f: Callable[[Any], Any]) -> FLarray:
"""Apply function over flarray contents, returns new instance"""
return FLarray(*mapIter(iter(self), f))
def map_update(self, f: Callable[[Any], Any]) -> None:
"""Apply function over flarray contents"""
for idx in range(self._size):
self._list[idx] = f(self._list[idx])
def flatMap(self, f: Callable[[Any], FLarray]) -> FLarray:
"""Apply function and flatten result, returns new instance"""
return FLarray(
*concatIters(
*mapIter(mapIter(iter(self), f), lambda x: iter(x))
)
)
def mergeMap(self, f: Callable[[Any], FLarray]) -> FLarray:
"""Apply function and flatten result, returns new instance"""
return FLarray(
*mergeIters(
*mapIter(mapIter(iter(self), f), lambda x: iter(x))
)
)
if __name__ == "__main__":
pass
Classes
class FLarray (*ds, size: int = 0, default: Any = None)
-
Class representing a fixed length array data structure of length > 0.
Permits storing None as a value.
Construct a fixed length array - guarnteed to be of length |size| for size != 0 - if size not indicated (or 0), size to data provided - if no data provided, return array with default value of size = 1 - assign missing values the default value - if size < 0, pad provided data on left or slice it on the right
Expand source code
class FLarray(): """Class representing a fixed length array data structure of length > 0. Permits storing None as a value. """ def __init__(self, *ds, size: int = 0, default: Any = None): """Construct a fixed length array - guarnteed to be of length |size| for size != 0 - if size not indicated (or 0), size to data provided - if no data provided, return array with default value of size = 1 - assign missing values the default value - if size < 0, pad provided data on left or slice it on the right """ dlist = list(ds) dsize = len(dlist) match (size, abs(size) == dsize, abs(size) > dsize): case (0, _, _): # default to the size of the data given if dsize > 0: self._size = dsize self._list = dlist else: # ensure flarray not empty self._size = 1 self._list = [default] case (_, True, _): # no size inconsistencies self._size = dsize self._list = dlist case (_, _, True): if size > 0: # pad higher indexes (on "right") self._size = size self._list = dlist + [default]*(size - dsize) else: # pad lower indexes (on "left") dlist.reverse() dlist += [default]*(-size - dsize) dlist.reverse() self._size = -size self._list = dlist + [default]*(size - dsize) case _: if size > 0: # take left slice, ignore extra data at end self._size = size self._list = dlist[0:size] else: # take right slice, ignore extra data at beginning self._size = -size self._list = dlist[dsize+size:] def __bool__(self): """Returns true if not all values evaluate as False""" for ii in range(self._size): if self._list[ii]: return True return False def __len__(self) -> int: """Returns the size of the flarray""" return self._size def __getitem__(self, index: int) -> Union[Any, Never]: # TODO: Does not like being given a slice ... research cnt = self._size if not -cnt <= index < cnt: l = -cnt h = cnt - 1 msg = f'fdArray index = {index} not between {l} and {h} while getting value' raise IndexError(msg) return self._list[index] def __setitem__(self, index: int, value: Any) -> Union[None, Never]: cnt = self._size if not -cnt <= index < cnt: l = -cnt h = cnt - 1 msg = f'fdArray index = {index} not between {l} and {h} while getting value' raise IndexError(msg) self._list[index] = value def __iter__(self): """Iterator yielding data currently stored in flarray""" currList = self._list.copy() for pos in range(self._size): yield currList[pos] def __reversed__(self): """Reverse iterate over the current state of the flarray""" for data in reversed(self._list.copy()): yield data def __eq__(self, other): """Returns True if all the data stored in both compare as equal. Worst case is O(n) behavior for the true case. """ if not isinstance(other, type(self)): return False return self._list == other._list def __repr__(self): """Display data in flarray""" listStrs = [] for data in self: listStrs.append(repr(data)) return "[| " + ", ".join(listStrs) + " |]" def copy(self) -> FLarray: """Return shallow copy of the flarray in O(n) time & space complexity""" return FLarray(*self) def map(self, f: Callable[[Any], Any]) -> FLarray: """Apply function over flarray contents, returns new instance""" return FLarray(*mapIter(iter(self), f)) def map_update(self, f: Callable[[Any], Any]) -> None: """Apply function over flarray contents""" for idx in range(self._size): self._list[idx] = f(self._list[idx]) def flatMap(self, f: Callable[[Any], FLarray]) -> FLarray: """Apply function and flatten result, returns new instance""" return FLarray( *concatIters( *mapIter(mapIter(iter(self), f), lambda x: iter(x)) ) ) def mergeMap(self, f: Callable[[Any], FLarray]) -> FLarray: """Apply function and flatten result, returns new instance""" return FLarray( *mergeIters( *mapIter(mapIter(iter(self), f), lambda x: iter(x)) ) )
Methods
def copy(self) ‑> FLarray
-
Return shallow copy of the flarray in O(n) time & space complexity
Expand source code
def copy(self) -> FLarray: """Return shallow copy of the flarray in O(n) time & space complexity""" return FLarray(*self)
def flatMap(self, f: Callable[[Any], FLarray]) ‑> FLarray
-
Apply function and flatten result, returns new instance
Expand source code
def flatMap(self, f: Callable[[Any], FLarray]) -> FLarray: """Apply function and flatten result, returns new instance""" return FLarray( *concatIters( *mapIter(mapIter(iter(self), f), lambda x: iter(x)) ) )
def map(self, f: Callable[[Any], Any]) ‑> FLarray
-
Apply function over flarray contents, returns new instance
Expand source code
def map(self, f: Callable[[Any], Any]) -> FLarray: """Apply function over flarray contents, returns new instance""" return FLarray(*mapIter(iter(self), f))
def map_update(self, f: Callable[[Any], Any]) ‑> None
-
Apply function over flarray contents
Expand source code
def map_update(self, f: Callable[[Any], Any]) -> None: """Apply function over flarray contents""" for idx in range(self._size): self._list[idx] = f(self._list[idx])
def mergeMap(self, f: Callable[[Any], FLarray]) ‑> FLarray
-
Apply function and flatten result, returns new instance
Expand source code
def mergeMap(self, f: Callable[[Any], FLarray]) -> FLarray: """Apply function and flatten result, returns new instance""" return FLarray( *mergeIters( *mapIter(mapIter(iter(self), f), lambda x: iter(x)) ) )