from __future__ import annotations
from functools import reduce
import numpy as np
import typing_extensions as tp
from numpy import char as npc
from static_frame.core.container_util import get_col_format_factory
from static_frame.core.node_selector import Interface, InterfaceBatch, TVContainer_co
from static_frame.core.util import (
DTYPE_BOOL,
DTYPE_OBJECT,
DTYPE_STR,
DTYPE_STR_KINDS,
NULL_SLICE,
OPERATORS,
TCallableAny,
TLabel,
TLocSelector,
TUFunc,
array_from_element_apply,
array_from_element_method,
)
if tp.TYPE_CHECKING:
from static_frame.core.batch import Batch
from static_frame.core.index_base import IndexBase
TNDArrayAny = np.ndarray[tp.Any, tp.Any]
TDtypeAny = np.dtype[tp.Any]
BlocksType = tp.Iterable[TNDArrayAny]
ToContainerType = tp.Callable[[tp.Iterator[TNDArrayAny]], TVContainer_co]
INTERFACE_STR = (
'__getitem__',
'capitalize',
'center',
'contains',
'count',
'decode',
'encode',
'endswith',
'find',
'format',
'index',
'isalnum',
'isalpha',
'isdecimal',
'isdigit',
'islower',
'isnumeric',
'isspace',
'istitle',
'isupper',
'ljust',
'len',
'lower',
'lstrip',
'partition',
'replace',
'rfind',
'rindex',
'rjust',
'rpartition',
'rsplit',
'rstrip',
'split',
'startswith',
'strip',
'swapcase',
'title',
'upper',
'zfill',
)
class InterfaceString(Interface, tp.Generic[TVContainer_co]):
# NOTE: based on https://numpy.org/doc/stable/reference/routines.char.html
__slots__ = (
'_blocks',
'_blocks_to_container',
'_ndim',
'_labels',
)
_INTERFACE = INTERFACE_STR
def __init__(
self,
blocks: BlocksType,
blocks_to_container: ToContainerType[TVContainer_co], # type: ignore[type-var]
ndim: int,
labels: tp.Sequence[TLabel] | IndexBase,
) -> None:
self._blocks: BlocksType = blocks
self._blocks_to_container: ToContainerType[TVContainer_co] = blocks_to_container
self._ndim: int = ndim
self._labels: tp.Sequence[TLabel] | IndexBase = labels
# ---------------------------------------------------------------------------
@staticmethod
def _process_blocks(
blocks: BlocksType,
func: TUFunc,
args: tp.Tuple[tp.Any, ...] = (),
astype_str: bool = True,
) -> tp.Iterator[TNDArrayAny]:
"""
Block-wise processing of blocks after optional string conversion. Non-string conversion is necessary for ``decode``.
"""
for block in blocks:
if astype_str and block.dtype.kind not in DTYPE_STR_KINDS:
block = block.astype(DTYPE_STR)
array = func(block, *args)
array.flags.writeable = False
yield array
@staticmethod
def _process_tuple_blocks(
*,
blocks: BlocksType,
method_name: str,
dtype: TDtypeAny,
args: tp.Tuple[tp.Any, ...] = (),
) -> tp.Iterator[TNDArrayAny]:
"""
Element-wise processing of a methods on objects in a block, with pre-insert conversion to a tuple.
"""
for block in blocks:
if block.dtype.kind not in DTYPE_STR_KINDS:
block = block.astype(DTYPE_STR)
# resultant array is immutable
array = array_from_element_method(
array=block,
method_name=method_name,
args=args,
dtype=dtype,
pre_insert=tuple,
)
yield array
@staticmethod
def _process_element_blocks(
*,
blocks: BlocksType,
method_name: str,
dtype: TDtypeAny,
args: tp.Tuple[tp.Any, ...] = (),
) -> tp.Iterator[TNDArrayAny]:
"""
Element-wise processing of a methods on objects in a block, with pre-insert conversion to a tuple.
"""
for block in blocks:
if block.dtype.kind not in DTYPE_STR_KINDS:
block = block.astype(DTYPE_STR)
# resultant array is immutable
array = array_from_element_method(
array=block,
method_name=method_name,
args=args,
dtype=dtype,
)
yield array
# ---------------------------------------------------------------------------
[docs]
def __getitem__(self, key: TLocSelector) -> TVContainer_co:
"""
Return a container with the provided selection or slice of each element.
"""
block_gen = self._process_element_blocks(
blocks=self._blocks,
method_name='__getitem__',
args=(key,),
dtype=DTYPE_STR,
)
return self._blocks_to_container(block_gen)
[docs]
def capitalize(self) -> TVContainer_co:
"""
Return a container with only the first character of each element capitalized.
"""
# return self._blocks_to_container(npc.capitalize(self._blocks()))
block_gen = self._process_blocks(self._blocks, npc.capitalize)
return self._blocks_to_container(block_gen)
[docs]
def center(self, width: int, fillchar: str = ' ') -> TVContainer_co:
"""
Return a container with its elements centered in a string of length ``width``.
"""
block_gen = self._process_blocks(self._blocks, npc.center, (width, fillchar))
return self._blocks_to_container(block_gen)
[docs]
def contains(self, item: str) -> TVContainer_co:
"""
Return a Boolean container showing True of item is a substring of elements.
"""
block_gen = self._process_element_blocks(
blocks=self._blocks,
method_name='__contains__',
args=(item,),
dtype=DTYPE_BOOL,
)
return self._blocks_to_container(block_gen)
[docs]
def count(
self, sub: str, start: int = 0, end: tp.Optional[int] = None
) -> TVContainer_co:
"""
Returns a container with the number of non-overlapping occurrences of substring sub in the optional range ``start``, ``end``.
"""
block_gen = self._process_blocks(self._blocks, npc.count, (sub, start, end))
return self._blocks_to_container(block_gen)
[docs]
def decode(
self,
encoding: tp.Optional[str] = None,
errors: tp.Optional[str] = None,
) -> TVContainer_co:
"""
Apply str.decode() to each element. Elements must be bytes.
"""
block_gen = self._process_blocks(
blocks=self._blocks,
func=npc.decode,
args=(encoding, errors),
astype_str=False, # needs to be bytes
)
return self._blocks_to_container(block_gen)
[docs]
def encode(
self,
encoding: tp.Optional[str] = None,
errors: tp.Optional[str] = None,
) -> TVContainer_co:
"""
Apply str.encode() to each element. Elements must be strings.
"""
block_gen = self._process_blocks(self._blocks, npc.encode, (encoding, errors))
return self._blocks_to_container(block_gen)
[docs]
def endswith(
self,
suffix: tp.Union[str, tp.Iterable[str]],
start: int = 0,
end: tp.Optional[int] = None,
) -> TVContainer_co:
"""
Returns a container with the number of non-overlapping occurrences of substring ``suffix`` (or an iterable of suffixes) in the optional range ``start``, ``end``.
"""
if isinstance(suffix, str):
block_iter = self._process_blocks(
self._blocks, npc.endswith, (suffix, start, end)
)
return self._blocks_to_container(block_iter)
def block_gen() -> tp.Iterator[TNDArrayAny]:
blocks_per_sub = (
self._process_blocks(self._blocks, npc.endswith, (sub, start, end))
for sub in suffix
)
func = OPERATORS['__or__']
for block_layers in zip(*blocks_per_sub):
array = reduce(func, block_layers)
array.flags.writeable = False
yield array
return self._blocks_to_container(block_gen())
[docs]
def find(
self, sub: str, start: int = 0, end: tp.Optional[int] = None
) -> TVContainer_co:
"""
For each element, return the lowest index in the string where substring ``sub`` is found.
"""
block_gen = self._process_blocks(self._blocks, npc.find, (sub, start, end))
return self._blocks_to_container(block_gen)
[docs]
def index(
self, sub: str, start: int = 0, end: tp.Optional[int] = None
) -> TVContainer_co:
"""
Like ``find``, but raises ``ValueError`` when the substring is not found.
"""
block_gen = self._process_blocks(self._blocks, npc.index, (sub, start, end))
return self._blocks_to_container(block_gen)
[docs]
def isalnum(self) -> TVContainer_co:
"""
Returns true for each element if all characters in the string are alphanumeric and there is at least one character, false otherwise.
"""
block_gen = self._process_blocks(self._blocks, npc.isalnum)
return self._blocks_to_container(block_gen)
[docs]
def isalpha(self) -> TVContainer_co:
"""
Returns true for each element if all characters in the string are alphabetic and there is at least one character, false otherwise.
"""
block_gen = self._process_blocks(self._blocks, npc.isalpha)
return self._blocks_to_container(block_gen)
[docs]
def isdecimal(self) -> TVContainer_co:
"""
For each element, return True if there are only decimal characters in the element.
"""
block_gen = self._process_blocks(self._blocks, npc.isdecimal)
return self._blocks_to_container(block_gen)
[docs]
def isdigit(self) -> TVContainer_co:
"""
Returns true for each element if all characters in the string are digits and there is at least one character, false otherwise.
"""
block_gen = self._process_blocks(self._blocks, npc.isdigit)
return self._blocks_to_container(block_gen)
[docs]
def islower(self) -> TVContainer_co:
"""
Returns true for each element if all cased characters in the string are lowercase and there is at least one cased character, false otherwise.
"""
block_gen = self._process_blocks(self._blocks, npc.islower)
return self._blocks_to_container(block_gen)
[docs]
def isnumeric(self) -> TVContainer_co:
"""
For each element in self, return True if there are only numeric characters in the element.
"""
block_gen = self._process_blocks(self._blocks, npc.isnumeric)
return self._blocks_to_container(block_gen)
[docs]
def isspace(self) -> TVContainer_co:
"""
Returns true for each element if there are only whitespace characters in the string and there is at least one character, false otherwise.
"""
block_gen = self._process_blocks(self._blocks, npc.isspace)
return self._blocks_to_container(block_gen)
[docs]
def istitle(self) -> TVContainer_co:
"""
Returns true for each element if the element is a titlecased string and there is at least one character, false otherwise.
"""
block_gen = self._process_blocks(self._blocks, npc.istitle)
return self._blocks_to_container(block_gen)
[docs]
def isupper(self) -> TVContainer_co:
"""
Returns true for each element if all cased characters in the string are uppercase and there is at least one character, false otherwise.
"""
block_gen = self._process_blocks(self._blocks, npc.isupper)
return self._blocks_to_container(block_gen)
[docs]
def len(self) -> TVContainer_co:
"""
Return the length of the string.
"""
block_gen = self._process_blocks(self._blocks, npc.str_len)
return self._blocks_to_container(block_gen)
[docs]
def ljust(self, width: int, fillchar: str = ' ') -> TVContainer_co:
"""
Return a container with its elements ljusted in a string of length ``width``.
"""
block_gen = self._process_blocks(self._blocks, npc.ljust, (width, fillchar))
return self._blocks_to_container(block_gen)
[docs]
def lower(self) -> TVContainer_co:
"""
Return an array with the elements of self converted to lowercase.
"""
block_gen = self._process_blocks(self._blocks, npc.lower)
return self._blocks_to_container(block_gen)
[docs]
def lstrip(
self,
chars: tp.Optional[str] = None,
) -> TVContainer_co:
"""
For each element, return a copy with the leading characters removed.
"""
block_gen = self._process_blocks(self._blocks, npc.lstrip, (chars,))
return self._blocks_to_container(block_gen)
[docs]
def partition(
self,
sep: str,
) -> TVContainer_co:
"""
Partition each element around ``sep``.
"""
# NOTE: py str.partition gives a tuple.
block_gen = self._process_tuple_blocks(
blocks=self._blocks,
method_name='partition',
args=(sep,),
dtype=DTYPE_OBJECT,
)
return self._blocks_to_container(block_gen)
[docs]
def replace(
self,
old: str,
new: str,
count: int = -1,
) -> TVContainer_co:
"""
Return a container with its elements replaced in a string of length ``width``.
"""
block_gen = self._process_blocks(self._blocks, npc.replace, (old, new, count))
return self._blocks_to_container(block_gen)
[docs]
def rfind(
self, sub: str, start: int = 0, end: tp.Optional[int] = None
) -> TVContainer_co:
"""
For each element, return the highest index in the string where substring ``sub`` is found, such that sub is contained within ``start``, ``end``.
"""
block_gen = self._process_blocks(self._blocks, npc.rfind, (sub, start, end))
return self._blocks_to_container(block_gen)
[docs]
def rindex(
self, sub: str, start: int = 0, end: tp.Optional[int] = None
) -> TVContainer_co:
"""
Like ``rfind``, but raises ``ValueError`` when the substring ``sub`` is not found.
"""
block_gen = self._process_blocks(self._blocks, npc.rindex, (sub, start, end))
return self._blocks_to_container(block_gen)
[docs]
def rjust(self, width: int, fillchar: str = ' ') -> TVContainer_co:
"""
Return a container with its elements rjusted in a string of length ``width``.
"""
block_gen = self._process_blocks(self._blocks, npc.rjust, (width, fillchar))
return self._blocks_to_container(block_gen)
[docs]
def rpartition(
self,
sep: str,
) -> TVContainer_co:
"""
Partition (split) each element around the right-most separator.
"""
# NOTE: py str.rpartition gives a tuple.
block_gen = self._process_tuple_blocks(
blocks=self._blocks,
method_name='rpartition',
args=(sep,),
dtype=DTYPE_OBJECT,
)
return self._blocks_to_container(block_gen)
[docs]
def rsplit(
self,
sep: str,
maxsplit: int = -1,
) -> TVContainer_co:
"""
For each element, return a tuple of the words in the string, using sep as the delimiter string.
"""
# NOTE: npc.rsplit gives an array of lists, so implement our own routine to get an array of tuples.
block_gen = self._process_tuple_blocks(
blocks=self._blocks,
method_name='rsplit',
args=(sep, maxsplit),
dtype=DTYPE_OBJECT,
)
return self._blocks_to_container(block_gen)
[docs]
def rstrip(
self,
chars: tp.Optional[str] = None,
) -> TVContainer_co:
"""
For each element, return a copy with the trailing characters removed.
"""
block_gen = self._process_blocks(self._blocks, npc.rstrip, (chars,))
return self._blocks_to_container(block_gen)
[docs]
def split(
self,
sep: str,
maxsplit: int = -1,
) -> TVContainer_co:
"""
For each element, return a tuple of the words in the string, using sep as the delimiter string.
"""
# NOTE: npc.split gives an array of lists, so implement our own routine to get an array of tuples.
block_gen = self._process_tuple_blocks(
blocks=self._blocks,
method_name='split',
args=(sep, maxsplit),
dtype=DTYPE_OBJECT,
)
return self._blocks_to_container(block_gen)
# splitlines: not likely useful
[docs]
def startswith(
self,
prefix: tp.Union[str, tp.Iterable[str]],
start: int = 0,
end: tp.Optional[int] = None,
) -> TVContainer_co:
"""
Returns a container with the number of non-overlapping occurrences of substring `prefix` (or an iterable of prefixes) in the optional range ``start``, ``end``.
"""
if isinstance(prefix, str):
block_iter = self._process_blocks(
self._blocks, npc.startswith, (prefix, start, end)
)
return self._blocks_to_container(block_iter)
def block_gen() -> tp.Iterator[TNDArrayAny]:
blocks_per_sub = (
self._process_blocks(self._blocks, npc.startswith, (sub, start, end))
for sub in prefix
)
func = OPERATORS['__or__']
for block_layers in zip(*blocks_per_sub):
array = reduce(func, block_layers)
array.flags.writeable = False
yield array
return self._blocks_to_container(block_gen())
[docs]
def strip(
self,
chars: tp.Optional[str] = None,
) -> TVContainer_co:
"""
For each element, return a copy with the leading and trailing characters removed.
"""
block_gen = self._process_blocks(self._blocks, npc.strip, (chars,))
return self._blocks_to_container(block_gen)
[docs]
def swapcase(self) -> TVContainer_co:
"""
Return a container with uppercase characters converted to lowercase and vice versa.
"""
block_gen = self._process_blocks(self._blocks, npc.swapcase)
return self._blocks_to_container(block_gen)
[docs]
def title(self) -> TVContainer_co:
"""
Return a container with uppercase characters converted to lowercase and vice versa.
"""
block_gen = self._process_blocks(self._blocks, npc.title)
return self._blocks_to_container(block_gen)
# translate: akward input
[docs]
def upper(self) -> TVContainer_co:
"""
Return a container with uppercase characters converted to lowercase and vice versa.
"""
block_gen = self._process_blocks(self._blocks, npc.upper)
return self._blocks_to_container(block_gen)
[docs]
def zfill(
self,
width: int,
) -> TVContainer_co:
"""
Return the string left-filled with zeros.
"""
block_gen = self._process_blocks(self._blocks, npc.zfill, (width,))
return self._blocks_to_container(block_gen)
class InterfaceBatchString(InterfaceBatch):
"""Alternate string interface specialized for the :obj:`Batch`."""
__slots__ = ('_batch_apply',)
_INTERFACE = INTERFACE_STR
def __init__(
self,
batch_apply: tp.Callable[[TCallableAny], 'Batch'],
) -> None:
self._batch_apply = batch_apply
# ---------------------------------------------------------------------------
[docs]
def __getitem__(self, key: TLocSelector) -> 'Batch':
"""
Return a container with the provided selection or slice of each element.
"""
return self._batch_apply(lambda c: c.via_str.__getitem__(key))
[docs]
def capitalize(self) -> 'Batch':
"""
Return a container with only the first character of each element capitalized.
"""
return self._batch_apply(lambda c: c.via_str.capitalize())
[docs]
def center(self, width: int, fillchar: str = ' ') -> 'Batch':
"""
Return a container with its elements centered in a string of length ``width``.
"""
return self._batch_apply(lambda c: c.via_str.center(width, fillchar))
[docs]
def count(self, sub: str, start: int = 0, end: tp.Optional[int] = None) -> 'Batch':
"""
Returns a container with the number of non-overlapping occurrences of substring sub in the optional range ``start``, ``end``.
"""
return self._batch_apply(lambda c: c.via_str.count(sub, start, end))
[docs]
def contains(
self,
item: str,
) -> 'Batch':
"""
Returns a container with the number of non-overlapping occurrences of substring sub in the optional range ``start``, ``end``.
"""
return self._batch_apply(lambda c: c.via_str.contains(item))
[docs]
def decode(
self,
encoding: tp.Optional[str] = None,
errors: tp.Optional[str] = None,
) -> 'Batch':
"""
Apply str.decode() to each element. Elements must be bytes.
"""
return self._batch_apply(lambda c: c.via_str.decode(encoding, errors))
[docs]
def encode(
self,
encoding: tp.Optional[str] = None,
errors: tp.Optional[str] = None,
) -> 'Batch':
"""
Apply str.encode() to each element. Elements must be strings.
"""
return self._batch_apply(lambda c: c.via_str.encode(encoding, errors))
[docs]
def endswith(
self,
suffix: tp.Union[str, tp.Iterable[str]],
start: int = 0,
end: tp.Optional[int] = None,
) -> 'Batch':
"""
Returns a container with the number of non-overlapping occurrences of substring ``suffix`` (or an iterable of suffixes) in the optional range ``start``, ``end``.
"""
return self._batch_apply(lambda c: c.via_str.endswith(suffix, start, end))
[docs]
def find(self, sub: str, start: int = 0, end: tp.Optional[int] = None) -> 'Batch':
"""
For each element, return the lowest index in the string where substring ``sub`` is found.
"""
return self._batch_apply(lambda c: c.via_str.find(sub, start, end))
[docs]
def index(self, sub: str, start: int = 0, end: tp.Optional[int] = None) -> 'Batch':
"""
Like ``find``, but raises ``ValueError`` when the substring is not found.
"""
return self._batch_apply(lambda c: c.via_str.index(sub, start, end))
[docs]
def isalnum(self) -> 'Batch':
"""
Returns true for each element if all characters in the string are alphanumeric and there is at least one character, false otherwise.
"""
return self._batch_apply(lambda c: c.via_str.isalnum())
[docs]
def isalpha(self) -> 'Batch':
"""
Returns true for each element if all characters in the string are alphabetic and there is at least one character, false otherwise.
"""
return self._batch_apply(lambda c: c.via_str.isalpha())
[docs]
def isdecimal(self) -> 'Batch':
"""
For each element, return True if there are only decimal characters in the element.
"""
return self._batch_apply(lambda c: c.via_str.isdecimal())
[docs]
def isdigit(self) -> 'Batch':
"""
Returns true for each element if all characters in the string are digits and there is at least one character, false otherwise.
"""
return self._batch_apply(lambda c: c.via_str.isdigit())
[docs]
def islower(self) -> 'Batch':
"""
Returns true for each element if all cased characters in the string are lowercase and there is at least one cased character, false otherwise.
"""
return self._batch_apply(lambda c: c.via_str.islower())
[docs]
def isnumeric(self) -> 'Batch':
"""
For each element in self, return True if there are only numeric characters in the element.
"""
return self._batch_apply(lambda c: c.via_str.isnumeric())
[docs]
def isspace(self) -> 'Batch':
"""
Returns true for each element if there are only whitespace characters in the string and there is at least one character, false otherwise.
"""
return self._batch_apply(lambda c: c.via_str.isspace())
[docs]
def istitle(self) -> 'Batch':
"""
Returns true for each element if the element is a titlecased string and there is at least one character, false otherwise.
"""
return self._batch_apply(lambda c: c.via_str.istitle())
[docs]
def isupper(self) -> 'Batch':
"""
Returns true for each element if all cased characters in the string are uppercase and there is at least one character, false otherwise.
"""
return self._batch_apply(lambda c: c.via_str.isupper())
[docs]
def len(self) -> 'Batch':
"""
Return the length of the string.
"""
return self._batch_apply(lambda c: c.via_str.len())
[docs]
def ljust(self, width: int, fillchar: str = ' ') -> 'Batch':
"""
Return a container with its elements ljusted in a string of length ``width``.
"""
return self._batch_apply(lambda c: c.via_str.ljust(width, fillchar))
[docs]
def lower(self) -> 'Batch':
"""
Return an array with the elements of self converted to lowercase.
"""
return self._batch_apply(lambda c: c.via_str.lower())
[docs]
def lstrip(
self,
chars: tp.Optional[str] = None,
) -> 'Batch':
"""
For each element, return a copy with the leading characters removed.
"""
return self._batch_apply(lambda c: c.via_str.lstrip(chars))
[docs]
def partition(
self,
sep: str,
) -> 'Batch':
"""
Partition each element around ``sep``.
"""
return self._batch_apply(lambda c: c.via_str.partition(sep))
[docs]
def replace(
self,
old: str,
new: str,
count: int = -1,
) -> 'Batch':
"""
Return a container with its elements replaced in a string of length ``width``.
"""
return self._batch_apply(lambda c: c.via_str.replace(old, new, count))
[docs]
def rfind(self, sub: str, start: int = 0, end: tp.Optional[int] = None) -> 'Batch':
"""
For each element, return the highest index in the string where substring ``sub`` is found, such that sub is contained within ``start``, ``end``.
"""
return self._batch_apply(lambda c: c.via_str.rfind(sub, start, end))
[docs]
def rindex(self, sub: str, start: int = 0, end: tp.Optional[int] = None) -> 'Batch':
"""
Like ``rfind``, but raises ``ValueError`` when the substring ``sub`` is not found.
"""
return self._batch_apply(lambda c: c.via_str.rindex(sub, start, end))
[docs]
def rjust(self, width: int, fillchar: str = ' ') -> 'Batch':
"""
Return a container with its elements rjusted in a string of length ``width``.
"""
return self._batch_apply(lambda c: c.via_str.rjust(width, fillchar))
[docs]
def rpartition(
self,
sep: str,
) -> 'Batch':
"""
Partition (split) each element around the right-most separator.
"""
return self._batch_apply(lambda c: c.via_str.rpartition(sep))
[docs]
def rsplit(
self,
sep: str,
maxsplit: int = -1,
) -> 'Batch':
"""
For each element, return a tuple of the words in the string, using sep as the delimiter string.
"""
return self._batch_apply(lambda c: c.via_str.rsplit(sep, maxsplit))
[docs]
def rstrip(
self,
chars: tp.Optional[str] = None,
) -> 'Batch':
"""
For each element, return a copy with the trailing characters removed.
"""
return self._batch_apply(lambda c: c.via_str.rstrip(chars))
[docs]
def split(
self,
sep: str,
maxsplit: int = -1,
) -> 'Batch':
"""
For each element, return a tuple of the words in the string, using sep as the delimiter string.
"""
return self._batch_apply(lambda c: c.via_str.split(sep, maxsplit))
# splitlines: not likely useful
[docs]
def startswith(
self,
prefix: tp.Union[str, tp.Iterable[str]],
start: int = 0,
end: tp.Optional[int] = None,
) -> 'Batch':
"""
Returns a container with the number of non-overlapping occurrences of substring `prefix` (or an iterable of prefixes) in the optional range ``start``, ``end``.
"""
return self._batch_apply(lambda c: c.via_str.startswith(prefix, start, end))
[docs]
def strip(
self,
chars: tp.Optional[str] = None,
) -> 'Batch':
"""
For each element, return a copy with the leading and trailing characters removed.
"""
return self._batch_apply(lambda c: c.via_str.strip(chars))
[docs]
def swapcase(self) -> 'Batch':
"""
Return a container with uppercase characters converted to lowercase and vice versa.
"""
return self._batch_apply(lambda c: c.via_str.swapcase())
[docs]
def title(self) -> 'Batch':
"""
Return a container with uppercase characters converted to lowercase and vice versa.
"""
return self._batch_apply(lambda c: c.via_str.title())
# translate: akward input
[docs]
def upper(self) -> 'Batch':
"""
Return a container with uppercase characters converted to lowercase and vice versa.
"""
return self._batch_apply(lambda c: c.via_str.upper())
[docs]
def zfill(
self,
width: int,
) -> 'Batch':
"""
Return the string left-filled with zeros.
"""
return self._batch_apply(lambda c: c.via_str.zfill(width))