diff --git a/satella/__init__.py b/satella/__init__.py index 40000fcff96dd0f693583448ad609c2bda34c9bf..d55a17067acd2aeca35267ae02b5f6855e0ffbd4 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.25.8a1' +__version__ = '2.25.8a2' diff --git a/satella/coding/sequences/__init__.py b/satella/coding/sequences/__init__.py index b6a6b0961d54b597ee58ed9128adc3f82e09f52e..8344ad91c901d60c1082c4a6e9865e7479ce3681 100644 --- a/satella/coding/sequences/__init__.py +++ b/satella/coding/sequences/__init__.py @@ -1,5 +1,5 @@ from .average import RollingArithmeticAverage -from .choose import choose, choose_one +from .choose import choose, choose_one, choose_with_index from .iterators import infinite_counter, take_n, is_instance, skip_first, zip_shifted, \ stop_after, iter_dict_of_list, shift, other_sequence_no_longer_than, count, even, \ odd, n_th, smart_enumerate, smart_zip, unique, ConstruableIterator, walk, length, map_list, \ @@ -9,7 +9,7 @@ from .sequences import is_last, add_next, half_cartesian, group_quantity, Multir infinite_iterator, filter_out_false, filter_out_nones, index_of, index_of_max __all__ = ['choose', 'choose_one', 'infinite_counter', 'take_n', 'is_instance', 'is_last', - 'add_next', 'ConstruableIterator', 'walk', 'length', 'smart_zip', + 'add_next', 'ConstruableIterator', 'walk', 'length', 'smart_zip', 'choose_with_index', 'half_cartesian', 'skip_first', 'zip_shifted', 'stop_after', 'group_quantity', 'iter_dict_of_list', 'shift', 'other_sequence_no_longer_than', 'count', 'n_th', 'even', 'odd', 'Multirun', 'smart_enumerate', 'unique', 'map_list', 'is_empty', diff --git a/satella/coding/sequences/choose.py b/satella/coding/sequences/choose.py index 9371db2f44d0034b72c591aa11fe074835180374..38c22fc99baff320d9c3122ee3aa414e37d307a1 100644 --- a/satella/coding/sequences/choose.py +++ b/satella/coding/sequences/choose.py @@ -17,6 +17,20 @@ def choose_one(filter_fun: Predicate[T], iterable: Iteratable) -> T: return choose(filter_fun, iterable, True) +def choose_with_index(filter_fun: Predicate[T], iterable: Iteratable) -> tp.Tuple[T, int]: + """ + Return a first found single value that exists in given iterable and filter_fun called on it returns True + + :param filter_fun: function that returns bool on the single value + :param iterable: iterable to examine + :returns: a tuple of (element, index) + :raises ValueError: element not found + """ + for i, item in enumerate(iterable): + if filter_fun(item): + return item, i + raise ValueError('No elements matching given filter seen') + def choose(filter_fun: Predicate[T], iterable: Iteratable, check_multiple: bool = False) -> T: """ diff --git a/tests/test_coding/test_sequences.py b/tests/test_coding/test_sequences.py index 042eb4a9bc8438573bdda35db723405d6238e968..66cb89d22f303b9be39c9732a8fb467e0ecaff97 100644 --- a/tests/test_coding/test_sequences.py +++ b/tests/test_coding/test_sequences.py @@ -7,12 +7,18 @@ from satella.coding.sequences import choose, choose_one, infinite_counter, take_ unique, length, map_list, smart_zip, is_empty, make_list, RollingArithmeticAverage, \ enumerate2, infinite_iterator, to_iterator, filter_out_false, filter_out_nones, \ index_of, index_of_max, try_close, f_range, iterate_callable, AlreadySeen, \ - append_sequence + append_sequence, choose_with_index from satella.coding.predicates import x class TestSequences(unittest.TestCase): + def test_check_with_index(self): + a = [1,2,3,4,5] + elem, i = choose_with_index(lambda a: a == 3) + self.assertEqual(elem, 3) + self.assertEqual(i, 2) + def test_append_sequence(self): a = [(1,), (2,), (3,)] self.assertEqual(list(append_sequence(a, 1, 2)), [(1, 1, 2), (2, 1, 2), (3, 1, 2)])