Source code for cardinal_pythonlib.lists

#!/usr/bin/env python
# cardinal_pythonlib/lists.py

"""
===============================================================================

    Original code copyright (C) 2009-2022 Rudolf Cardinal (rudolf@pobox.com).

    This file is part of cardinal_pythonlib.

    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

        https://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.

===============================================================================

**Functions for dealing with lists (and generators/iterables).**

"""

from collections import Counter
from operator import itemgetter
from typing import Any, Callable, Iterable, List, Sequence, Tuple


# =============================================================================
# Lists and similar
# =============================================================================


[docs]def contains_duplicates(values: Iterable[Any]) -> bool: """ Does the iterable contain any duplicate values? """ for v in Counter(values).values(): if v > 1: return True return False
[docs]def index_list_for_sort_order( x: List[Any], key: Callable[[Any], Any] = None, reverse: bool = False ) -> List[int]: """ Returns a list of indexes of ``x``, IF ``x`` WERE TO BE SORTED. Args: x: data key: function to be applied to the data to generate a sort key; this function is passed as the ``key=`` parameter to :func:`sorted`; the default is ``itemgetter(1)`` reverse: reverse the sort order? Returns: list of integer index values Example: .. code-block:: python z = ["a", "c", "b"] index_list_for_sort_order(z) # [0, 2, 1] index_list_for_sort_order(z, reverse=True) # [1, 2, 0] q = [("a", 9), ("b", 8), ("c", 7)] index_list_for_sort_order(q, key=itemgetter(1)) """ def key_with_user_func(idx_val: Tuple[int, Any]): return key(idx_val[1]) if key: sort_key = key_with_user_func # see the simpler version below else: sort_key = itemgetter(1) # enumerate, below, will return tuples of (index, value), so # itemgetter(1) means sort by the value index_value_list = sorted(enumerate(x), key=sort_key, reverse=reverse) return [i for i, _ in index_value_list]
[docs]def sort_list_by_index_list(x: List[Any], indexes: List[int]) -> None: """ Re-orders ``x`` by the list of ``indexes`` of ``x``, in place. Example: .. code-block:: python from cardinal_pythonlib.lists import sort_list_by_index_list z = ["a", "b", "c", "d", "e"] sort_list_by_index_list(z, [4, 0, 1, 2, 3]) z # ["e", "a", "b", "c", "d"] """ x[:] = [x[i] for i in indexes]
[docs]def flatten_list(x: List[Any]) -> List[Any]: """ Converts a list of lists into a flat list. Args: x: list of lists Returns: flat list As per https://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python """ # noqa return [item for sublist in x for item in sublist]
[docs]def unique_list(seq: Iterable[Any]) -> List[Any]: """ Returns a list of all the unique elements in the input list. Args: seq: input list Returns: list of unique elements As per https://stackoverflow.com/questions/480214/how-do-you-remove-duplicates-from-a-list-in-whilst-preserving-order """ # noqa seen = set() seen_add = seen.add return [x for x in seq if not (x in seen or seen_add(x))]
[docs]def filter_unique(seq: Iterable[Any]) -> Iterable[Any]: """ Filters the input sequence, yielding only unique values. """ seen = set() seen_add = seen.add # fractionally faster to look it up once for x in seq: if x not in seen: seen_add(x) yield x
[docs]def chunks(x: List[Any], n: int) -> Iterable[List[Any]]: """ Yield successive ``n``-sized chunks from the list ``x``. Args: x: input list n: chunk size Yields: successive chunks of size ``n`` """ for i in range(0, len(x), n): yield x[i : i + n]
[docs]def count_bool(blist: Iterable[Any]) -> int: """ Counts the number of "truthy" members of the input list. Args: blist: list of booleans or other truthy/falsy things Returns: number of truthy items """ return sum([1 if x else 0 for x in blist])
[docs]def delete_elements_by_index(x: List[Any], indices: Sequence[int]) -> None: """ Deletes (in place) objects from a list, by (zero-based) index values. Args: x: list to modify indices: zero-based index values at which to delete elements of x Note: - If you pass duplicate values, unexpected results will occur. - If you pass out-of-bound indices, :exc:`IndexError` will be raised. After: - https://thispointer.com/python-remove-elements-from-list-by-index/ - https://stackoverflow.com/questions/11520492/difference-between-del-remove-and-pop-on-lists - https://stackoverflow.com/questions/627435/how-to-remove-an-element-from-a-list-by-index """ # noqa for i in sorted(indices, reverse=True): # work from high to low del x[i]