"""
.. _utils-operations:
Operations Utilities
--------------------
A collection classes with utility functions to perform common / convenient
operations on the classic Python structures.
.. warning::
This module contains un-used code and will be removed in a future release.
"""
import copy
import itertools
import math
import random
import re
from functools import reduce
from typing import Callable, Dict, List, Sequence, Tuple, Union
[docs]class ListOperations:
"""
.. versionadded:: 0.2.0
A class to group some common / useful operations on lists or sequences.
"""
[docs] @staticmethod
def all_unique(sequence: Sequence) -> bool:
"""
.. versionadded:: 0.2.0
Returns `True` if all the values in a flat list are unique, `False` otherwise.
Args:
sequence (Sequence): a sequence of elements.
Returns:
`True` if all elements are unique, `False` otherwise.
Example:
.. code-block:: python
ListOperations.all_unique([1, 2, 3, 5, 12, 0])
# returns True
"""
return len(sequence) == len(set(sequence))
[docs] @staticmethod
def average_by(sequence: Sequence, function: Callable = lambda x: x) -> float:
"""
.. versionadded:: 0.2.0
Returns the average of *sequence* after mapping each element to a value using the
provided function. Use `map` to map each element to the value returned by *function*.
Uses `sum` to sum all of the mapped values, divided by `len`.
Args:
sequence (Sequence): a sequence of elements.
function (Callable): function to apply to elements of the sequence.
Returns:
The average of each element's result through *function*.
Example:
.. code-block:: python
ListOperations.average_by(
[{'n': 4}, {'n': 2}, {'n': 8}, {'n': 6}], lambda x: x['n']
)
# returns 5.0
"""
return float(sum(map(function, sequence), 0.0) / len(sequence))
[docs] @staticmethod
def bifurcate(sequence: Sequence, filters: List[bool]) -> Sequence:
"""
.. versionadded:: 0.2.0
Splits values into two groups. If an element in filter is `True`, the corresponding
element in the collection belongs to the first group; otherwise, it belongs to the
second group. Uses list comprehension and `enumerate` to add elements to groups,
based on *filter*.
Args:
sequence (Sequence): a sequence of elements.
filters (List[bool]): a list of booleans.
Returns:
A list of two lists, one for each boolean output of the filters.
Example:
.. code-block:: python
ListOperations.bifurcate(['beep', 'boop', 'foo', 'bar'], [True, True, False, True])
# returns [['beep', 'boop', 'bar'], ['foo']]
"""
return [
[x for i, x in enumerate(sequence) if filters[i]],
[x for i, x in enumerate(sequence) if not filters[i]],
]
[docs] @staticmethod
def bifurcate_by(sequence: Sequence, function: Callable) -> list:
"""
.. versionadded:: 0.2.0
Splits values into two groups according to a function, which specifies which group an
element in the input sequence belongs to. If the function returns `True`, the element
belongs to the first group; otherwise it belongs to the second group. Uses list
comprehension to add elements to groups, based on *function*.
Args:
sequence (Sequence): a sequence of elements.
function (Callable): a callable on the elements of *sequence*, that should return a
boolean.
Returns:
A list of two lists, as groups of elements of *sequence* classified depending on their
result when passed to *function*.
Example:
.. code-block:: python
ListOperations.bifurcate_by(list(range(5)), lambda x: x % 2 == 0)
# returns [[0, 2, 4], [1, 3]]
"""
return [[x for x in sequence if function(x)], [x for x in sequence if not function(x)]]
[docs] @staticmethod
def chunk_list(sequence: Sequence, size: int) -> Sequence:
"""
.. versionadded:: 0.2.0
Chunks a sequence into smaller lists of a specified size. If the size is bigger
than that of *sequence*, return *sequence* to avoid unnecessary nesting. Uses
`list` and `range` to create a list of the desired size. Uses `map` on that
list and fills it with splices of *sequence*. Finally, returns the created list.
Args:
sequence (Sequence): a sequence of elements.
size (int): the size of the wanted sublists.
Returns:
A `list` of lists of length `size` (except maybe the last element), with
elements from *sequence*.
Example:
.. code-block:: python
ListOperations.chunk_list(list(range(10)), 3)
# returns [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
"""
if size > len(sequence):
return sequence
return list(map(lambda x: sequence[x * size : x * size + size], list(range(math.ceil(len(sequence) / size)))))
[docs] @staticmethod
def deep_flatten(sequence: Sequence) -> list:
"""
.. versionadded:: 0.2.0
Recursively deep flattens *sequence*, no matter the nesting levels.
Args:
sequence (Sequence): a sequence of elements.
Returns:
A `list` with all elements of *sequence*, but flattened.
Example:
.. code-block:: python
ListOperations.deep_flatten([["a", "b"], [1, 2], None, [True, False]])
# returns ["a", "b", 1, 2, None True, False]
"""
return (
[elem for sublist in sequence for elem in ListOperations.deep_flatten(sublist)]
if isinstance(sequence, list)
else [sequence]
)
[docs] @staticmethod
def eval_none(sequence: Sequence, function: Callable = lambda x: not not x) -> bool:
"""
.. versionadded:: 0.2.0
Returns `False` if the provided *function* returns `True` for at least one element
in *sequence*, `True` otherwise. Iterates over *sequence* to test if every element
returns `False` based on function. Omit the seconds argument, *function*, to check
if all elements are `False`.
Args:
sequence (Sequence): a sequence of elements.
function (Callable): a callable on elements of *sequence* that should return
a boolean.
Returns:
A boolean. See first line of docstring.
Examples:
.. code-block:: python
ListOperations.eval_none([0, 0, 1, 0], lambda x: x >= 2)
# returns True
.. code-block:: python
ListOperations.eval_none([0, 1, 2, 0], lambda x: x >= 2)
# returns False
"""
return not any(map(function, sequence))
[docs] @staticmethod
def eval_some(sequence: Sequence, function: Callable = lambda x: not not x) -> bool:
"""
.. versionadded:: 0.2.0
Returns `True` if the provided *function* returns `True` for at least one element in
*sequence*, `False` otherwise. Iterates over the elements of *sequence* to test if every
element returns `True` based on *function*. Omit the seconds argument, *function*, to check
if all elements are `True`.
Args:
sequence (Sequence): a sequence of elements.
function (Callable): a callable on elements of *sequence* that should return a boolean.
Returns:
A boolean. See first line of docstring.
Examples:
.. code-block:: python
ListOperations.eval_some([0, 1, 2, 0], lambda x: x >= 2)
# returns True
.. code-block:: python
ListOperations.eval_some([0, 0, 1, 0], lambda x: x >= 2)
# returns False
"""
return any(map(function, sequence))
[docs] @staticmethod
def get_indices(element, sequence: Sequence) -> List[int]:
"""
.. versionadded:: 0.2.0
Return all array indices at which *element* is located.
Args:
element: any reference element to check.
sequence (Sequence): a sequence containing objects comparable to *elements*.
A `string` can be compared to an `int` in Python, custom objects probably
won't be comparable.
Returns:
A `list` of all indices at which *element* is found in *sequence*. Empty list if
*element* is not present in *sequence* at all.
Example:
.. code-block:: python
ListOperations.get_indices(0, [0, 1, 3, 5, 7, 3, 9, 0, 0, 5, 3, 2])
# returns [0, 7, 8]
"""
return [i for (y, i) in zip(sequence, range(len(sequence))) if element == y]
[docs] @staticmethod
def group_by(sequence: Sequence, function: Callable) -> Dict[str, list]:
"""
.. versionadded:: 0.2.0
Groups the elements of *sequence* based on the given *function*. Uses `list` in
combination with `map` and *function* to map the values of *sequence* to the
keys of an object. Uses list comprehension to map each element to the appropriate key.
Args:
sequence (Sequence): a sequence of elements.
function (Callable): a callable on the elements of *sequence* that should return a
boolean.
Returns:
A `dict` with keys `True` and `False`, each having as value a list of all elements of
*sequence* that were evaluated to respectively `True` or `False` through *function*.
Example:
.. code-block:: python
ListOperations.group_by(list(range(5)), lambda x: x % 2 == 0)
# returns {True: [0, 2, 4], False: [1, 3]}
"""
groups = {}
for key in list(map(function, sequence)):
groups[key] = [item for item in sequence if function(item) == key]
return groups
[docs] @staticmethod
def has_duplicates(sequence: Sequence) -> bool:
"""
.. versionadded:: 0.2.0
Returns `True` if there are duplicate values in *sequence*, `False` otherwise.
Uses `set` on the given *sequence* to remove duplicates, then compares its length
with that of *sequence*.
Args:
sequence (Sequence): a sequence of elements.
Returns:
A boolean indicating the presence of duplicates in *sequence*.
Example:
.. code-block:: python
ListOperations.has_duplicates([1, 2, 1])
# returns True
"""
return len(sequence) != len(set(sequence))
[docs] @staticmethod
def sample(sequence: Sequence) -> list:
"""
.. versionadded:: 0.2.0
Returns a random element from *sequence*.
Args:
sequence (Sequence): a sequence of elements.
Returns:
A random element from *sequence* in a list (to manage potentially
nested sequences as input).
Examples:
.. code-block:: python
ListOperations.sample(["a", "b", 1, 2, False])
# returns 2
"""
return sequence[random.randint(0, len(sequence) - 1)]
[docs] @staticmethod
def sanitize_list(sequence: Sequence) -> list:
"""
.. versionadded:: 0.2.0
Removes falsey values from a *sequence*. Uses `filter` to filter out falsey
values (`False`, `None`, `0`, and `""`).
Args:
sequence (Sequence): a sequence of elements.
Returns:
The sequence without falsey values.
Example:
.. code-block:: python
ListOperations.sanitize_list([1, False, "a", 2, "", None, 6, 0])
# returns [1, "a", 2, 6]
"""
return list(filter(bool, sequence))
[docs] @staticmethod
def shuffle(sequence: Sequence) -> Sequence:
"""
.. versionadded:: 0.2.0
Randomizes the order of the elements in *sequence*, returning a new `list`.
Uses an improved version of the
`Fisher-Yates algorithm <https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle>`_
to reorder the elements.
Args:
sequence (Sequence): a sequence of elements.
Returns:
A copy of *sequence* with original elements at a random index.
Examples:
.. code-block:: python
ListOperations.shuffle(["a", "b", 1, 2, False])
# returns ['b', 1, False, 2, 'a']
"""
temp_list = copy.deepcopy(sequence)
amount_to_shuffle = len(temp_list)
while amount_to_shuffle > 1:
rand_index = int(math.floor(random.random() * amount_to_shuffle))
amount_to_shuffle -= 1
temp_list[rand_index], temp_list[amount_to_shuffle] = (
temp_list[amount_to_shuffle],
temp_list[rand_index],
)
return temp_list
[docs] @staticmethod
def spread(sequence: Sequence) -> list:
"""
.. versionadded:: 0.2.0
Flattens the provided *sequence*, by spreading its elements into a new `list`.
Loops over elements, uses `list.extend` if the element is a list, `list.append`
otherwise. This might look like `~.ListOperations.deep_flatten` but is a subset
of its functionality, and is used in `~.ListOperations.deep_flatten`.
.. warning::
This only works if all elements in *sequence* are iterables.
Args:
sequence (Sequence): a sequence of elements.
Returns:
The sequence flattened, see first docstring sentence.
Example:
.. code-block:: python
ListOperations.spread([list(range(5)), list(range(5))])
# returns [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
"""
return list(itertools.chain.from_iterable(sequence))
[docs] @staticmethod
def symmetric_difference_by(seq_1: Sequence, seq_2: Sequence, function: Callable) -> list:
"""
.. versionadded:: 0.2.0
Returns the `symmetric difference <https://en.wikipedia.org/wiki/Symmetric_difference>`_
of the provided sequences, after applying the provided *function* to each element of both.
Creates a `set` by applying *function* to each element in each sequence, then uses list
comprehension in combination with *function* on each one to only keep values not contained
in the previously created set of the other.
Args:
lst_1 (Sequence): a sequence of elements.
lst_2 (Sequence): a sequence of elements.
function (Callable): a callable on elements of *seq_1* and *seq_2*.
Returns:
A `list`, see first docstring sentence reference.
Examples:
.. code-block:: python
ListOperations.symmetric_difference_by([2.1, 1.2], [2.3, 3.4], math.floor)
# returns [1.2, 3.4]
.. code-block:: python
ListOperations.symmetric_difference_by([2.1, 1.2], [0.5, 1.2], lambda x: x >= 2)
# returns [2.1]
"""
_lst_1, _lst_2 = set(map(function, seq_1)), set(map(function, seq_2))
return [item for item in seq_1 if function(item) not in _lst_2] + [
item for item in seq_2 if function(item) not in _lst_1
]
[docs] @staticmethod
def union_by(seq_1: Sequence, seq_2: Sequence, function: Callable) -> list:
"""
.. versionadded:: 0.2.0
Returns every element that exists in any of the two sequences once, after
applying the provided *function* to each element of both. This is the
`set theory union <https://en.wikipedia.org/wiki/Union_(set_theory)>`_ of the
two sequences, but based on the results of applying *function* to each.
.. note::
Python's `set` is strange in how is gives output, so this function sorts the
final list before returning it, in order to give it predictable behavior.
Creates a `set` by applying *function* to each element in *seq_1*, then uses list
comprehension in combination with *function* on *seq_2* to only keep values not
contained in the previously created set, _lst_1. Finally, create a set from the
previous result and _lst_1, and transform it into a `list`.
Args:
lst_1 (Sequence): a sequence of elements.
lst_2 (Sequence): a sequence of elements.
function (Callable): a callable on elements of *seq_1* and *seq_2*.
Returns:
A `list`, see first docstring sentence reference.
Example:
.. code-block:: python
ListOperations.union_by([2.1], [1.2, 2.3], math.floor)
# returns [1.2, 2.1]
"""
_lst_1 = set(map(function, seq_1))
return sorted(list(set(seq_1 + [item for item in seq_2 if function(item) not in _lst_1])))
[docs] @staticmethod
def zipper(*args, fillvalue=None) -> list:
"""
.. versionadded:: 0.2.0
Creates a `list` of lists of elements, each internal list being a grouping based
on the position of elements in the original sequences. Essentially, a list containing:
* a first list with all first elements,
* then a second list with all second elements, etc.
Uses `max` combined with list comprehension to get the length of the longest list in
the arguments. Loops for max_length times grouping elements. If lengths of sequences
vary, uses *fill_value* to adjust the smaller ones (defaults to `None`).
Args:
*args: a number (>= 2) of different iterables.
fillvalue: value to use in case of length mismatch.
Returns:
A `list` with the proper level of nesting, and original elements zipped.
Example:
.. code-block:: python
ListOperations.zipper([1, 2, 3], [2, 5, 3, 7], ["a", "b", "c"])
# returns [[1, 2, 'a'], [2, 5, 'b'], [3, 3, 'c'], [None, 7, None]]
"""
max_length = max(len(lst) for lst in args)
return [[args[k][i] if i < len(args[k]) else fillvalue for k in range(len(args))] for i in range(max_length)]
[docs]class MiscellaneousOperations:
"""
.. versionadded:: 0.2.0
A class to group some misc. operations that don't pertain to classic structures.
"""
[docs] @staticmethod
def longest_item(*args):
"""
.. versionadded:: 0.2.0
Takes any number of iterable objects, or objects with a length property, and returns the
longest one. If multiple objects have the same length, the first one will be returned.
Usess `max` with `len` as the key to return the item with the greatest length.
Args:
*args: any number (>= 2) of iterables.
Returns:
The longest elements of provided iterables.
Example:
.. code-block:: python
MiscellaneousOperations.longest_item(
list(range(5)), list(range(100)), list(range(50))
)
# returns list(range(100))
"""
return max(args, key=len)
[docs] @staticmethod
def map_values(obj: dict, function: Callable) -> dict:
"""
.. versionadded:: 0.2.0
Creates an new `dict` with the same keys as the provided *obj*,
and values generated by running *function* on the *obj*'s values.
Uses `dict.keys` to iterate over the object's keys, assigning the
values produced by *function* to each key of a new object.
Args:
obj (dict): a dictionary.
function (Callable): a callable on values of `obj`.
Returns:
A new dictionary with the results.
Example:
.. code-block:: python
MiscellaneousOperations.map_values(
{"a": list(range(5)), "b": list(range(10)), "c": list(range(15))},
lambda x: len(x)
)
# returns {"a": 5, "b": 10, "c": 15}
"""
ret = {}
for key in obj:
ret[key] = function(obj[key])
return ret
[docs]class NumberOperations:
"""
.. versionadded:: 0.2.0
A class to group some common / useful operations on numbers.
"""
[docs] @staticmethod
def clamp_number(num: Union[int, float], a_val: Union[int, float], b_val: Union[int, float]) -> Union[int, float]:
"""
.. versionadded:: 0.2.0
Clamps *num* within the inclusive range specified by the boundary values *a_val*
and *b_val*. If *num* falls within the range, returns *num*. Otherwise, return the
nearest number in the range.
Args:
num (Union[int, float]): a number (float / int)
a_val (Union[int, float]): a number (float / int)
b_val (Union[int, float]): a number (float / int)
Returns:
A number (float / int), being the nearest to *num* in the range [*a_val*, *b_val*].
Examples:
.. code-block:: python
NumberOperations.clamp_number(17, 4, 5)
# returns 5
.. code-block:: python
NumberOperations.clamp_number(23, 20, 30)
# returns 23
"""
return max(min(num, max(a_val, b_val)), min(a_val, b_val))
[docs] @staticmethod
def degrees_to_radians(
deg_value: Union[int, float], decompose: bool = False
) -> Union[Tuple[float, str, str], int, float]:
"""
.. versionadded:: 0.2.0
Converts an angle from degrees to radians. Uses `math.pi` and the degree
to radian formula to convert the provided *deg_value*.
Args:
deg_value (Union[int, float]): angle value in degrees.
decompose (bool): boolean option to return a more verbose result. Defaults to `False`.
Returns:
The angle value in radians.
Examples:
.. code-block:: python
NumberOperations.degrees_to_radians(160)
# returns 2.792526803190927
.. code-block:: python
NumberOperations.degrees_to_radians(360, decompose=True)
# returns (2, "pi", "rad")
"""
if decompose:
return deg_value / 180, "pi", "rad"
return (deg_value * math.pi) / 180.0
[docs] @staticmethod
def greatest_common_divisor(sequence: Sequence) -> Union[int, float]:
"""
.. versionadded:: 0.2.0
Calculates the greatest common divisor of a sequence of numbers.
Uses `reduce` and `math.gcd` over the given *sequence*.
Args:
sequence (Sequence): a sequence of numbers (floats are advised against as this would
become a very heavy computation).
Returns:
The greatest common divisor of all elements in *sequence*.
Examples:
.. code-block:: python
NumberOperations.greatest_common_divisor([54, 24])
# returns 6
.. code-block:: python
NumberOperations.greatest_common_divisor([30, 132, 378, 582, 738])
# returns 6
"""
return reduce(math.gcd, sequence)
[docs] @staticmethod
def is_divisible_by(dividend: Union[int, float], divisor: Union[int, float]) -> bool:
"""
.. versionadded:: 0.2.0
Checks if the first numeric argument is divisible by the second one.
Uses the modulo operator (`%`) to check if the remainder is equal to 0.
Args:
dividend (Union[int, float]): a number.
divisor (Union[int, float]): a number.
Returns:
A boolean stating if *dividend* can be divided by *divisor*.
Examples:
.. code-block:: python
NumberOperations.is_divisible_by(35, 15)
# returns False
"""
return dividend % divisor == 0
[docs] @staticmethod
def least_common_multiple(*args) -> int:
"""
.. versionadded:: 0.2.0
Returns the least common multiple of two or more numbers. Defines a function, spread,
that uses either `list.extend` or `list.append` on each element of s sequence to flatten
it. Uses `math.gcd` and lcm(x,y) = (x * y) / gcd(x,y) to determine the least common multiple.
Args:
*args: any number (>= 2) of numbers (floats are advised against as this would become a
very heavy computation).
Returns:
The least common multiple of all provided numbers.
Examples:
.. code-block:: python
NumberOperations.least_common_multiple(4, 5)
# returns 20
.. code-block:: python
NumberOperations.least_common_multiple(2, 5, 17, 632)
# returns 53720
"""
numbers = list(ListOperations.spread(list(args)))
def _lcm(number1, number2):
"""A least common multiple method for two numbers only"""
return int(number1 * number2 / math.gcd(number1, number2))
return reduce(lambda x, y: _lcm(x, y), numbers)
[docs] @staticmethod
def radians_to_degrees(rad_value: Union[int, float]) -> Union[int, float]:
"""
.. versionadded:: 0.2.0
Converts an angle from radians to degrees. Uses `math.pi` and the radian
to degree formula to convert the provided *rad_value*.
Args:
rad_value (Union[int, float]): angle value in degrees.
Returns:
The angle value in degrees.
Examples:
.. code-block:: python
NumberOperations.radians_to_degrees(2* math.pi)
# returns 360
.. code-block:: python
NumberOperations.radians_to_degrees(2.710)
# returns 155.2715624804531
"""
return (rad_value * 180.0) / math.pi
[docs]class StringOperations:
"""
.. versionadded:: 0.2.0
A class to group some common / useful operations on strings.
"""
[docs] @staticmethod
def camel_case(text: str) -> str:
"""
.. versionadded:: 0.2.0
Converts a `string` to camelCase. Breaks the string into words and combines
them capitalizing the first letter of each word, using a regexp, `title` and `lower`.
Args:
text (str): a string.
Returns:
The same `string` best adapted to camelCase.
Examples:
.. code-block:: python
StringOperations.camel_case("a_snake_case_name")
# returns "aSnakeCaseName"
.. code-block:: python
StringOperations.camel_case("A Title Case Name")
# returns "aTitleCaseName"
"""
text = re.sub(r"(\s|_|-)+", " ", text).title().replace(" ", "")
return text[0].lower() + text[1:]
[docs] @staticmethod
def capitalize(text: str, lower_rest: bool = False) -> str:
"""
.. versionadded:: 0.2.0
Capitalizes the first letter of a `string`, eventually lowers the rest of it.
Omit the *lower_rest* parameter to keep the rest of the string intact, or set
it to `True` to convert to lowercase.
Args:
text (str): a string.
lower_rest (bool): boolean option to lower all elements starting from the second.
Returns:
The `string`, capitalized.
Examples:
.. code-block:: python
StringOperations.capitalize("astringtocapitalize")
# returns "Astringtocapitalize"
.. code-block:: python
StringOperations.capitalize("astRIngTocApItalizE", lower_rest=True)
# returns "Astringtocapitalize"
"""
return text[:1].upper() + (text[1:].lower() if lower_rest else text[1:])
[docs] @staticmethod
def is_anagram(str_1: str, str_2: str) -> bool:
"""
.. versionadded:: 0.2.0
Checks if a `string` is an anagram of another string (case-insensitive,
ignores spaces, punctuation and special characters). Uses `str.replace`
to remove spaces from both strings. Compares the lengths of the two strings,
return `False` if they are not equal. Uses `sorted` on both strings and
compares the results.
Args:
str_1 (str): a string.
str_2 (str): a string.
Returns:
A boolean stating whether `str_1` is an anagram of `str_2` or not.
Examples:
.. code-block:: python
StringOperations.is_anagram("Tom Marvolo Riddle", "I am Lord Voldemort")
# returns True
.. code-block:: python
StringOperations.is_anagram("A first string", "Definitely not an anagram")
# returns False
"""
_str1, _str2 = (
str_1.replace(" ", "").replace("'", ""),
str_2.replace(" ", "").replace("'", ""),
)
return sorted(_str1.lower()) == sorted(_str2.lower())
[docs] @staticmethod
def is_palindrome(text: str) -> bool:
"""
.. versionadded:: 0.2.0
Returns `True` if the given string is a palindrome, `False` otherwise.
Uses `str.lower` and `re.sub` to convert to lowercase and remove non-alphanumeric
characters from the given string. Then compare the new string with its reverse.
Args:
text (str): a string.
Returns:
A boolean stating whether *text* is a palindrome or not.
Examples:
.. code-block:: python
StringOperations.is_palindrome("racecar")
# returns True
.. code-block:: python
StringOperations.is_palindrome("definitelynot")
# returns False
"""
s_reverse = re.sub(r"[\W_]", "", text.lower())
return s_reverse == s_reverse[::-1]
[docs] @staticmethod
def kebab_case(text: str) -> str:
"""
.. versionadded:: 0.2.0
Converts a string to kebab-case. Breaks the string into words and
combines them adding `-` as a separator, using a regexp.
Args:
text (str): a string.
Returns:
The same string best adapted to kebab_case.
Examples:
.. code-block:: python
StringOperations.kebab_case("camel Case")
# returns "camel-case"
.. code-block:: python
StringOperations.kebab_case("snake_case")
# returns "snake-case"
"""
return re.sub(
r"(\s|_|-)+",
"-",
re.sub(
r"[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+",
lambda mo: mo.group(0).lower(),
text,
),
)
[docs] @staticmethod
def snake_case(text: str) -> str:
"""
.. versionadded:: 0.2.0
Converts a string to snake_case. Breaks the string into words and
combines them adding `_` as a separator, using a regexp.
Args:
text (str): a string.
Returns:
The same string best adapted to snake_case.
Examples:
.. code-block:: python
StringOperations.snake_case("A bunch of words")
# returns "a_bunch_of_words"
.. code-block:: python
StringOperations.snake_case("camelCase")
# returns "camelcase"
"""
return re.sub(
r"(\s|_|-)+",
"_",
re.sub(
r"[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+",
lambda mo: mo.group(0).lower(),
text,
),
)