Skip to content

Instantly share code, notes, and snippets.

@markusand
Last active December 28, 2023 01:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markusand/fa88a739152925308d44b120d75bc8ec to your computer and use it in GitHub Desktop.
Save markusand/fa88a739152925308d44b120d75bc8ec to your computer and use it in GitHub Desktop.
Set of functional programming utilities for working with lists.
""" Functional module """
from typing import List, Dict, Callable, TypeVar, Optional
T = TypeVar('T')
U = TypeVar('U')
def map(predicate: Callable[[T], U], items: List[T]) -> List[U]:
'''
Applies a given function to all items in the list and returns a new list
containing the results.
Args:
predicate (Callable[[T], U]): The function to be applied to each item in the list.
items (List[T]): The list of items to be processed.
Returns:
List[U]: A new list containing the results of applying the function to each item.
'''
return [predicate(item) for item in items]
def reduce(predicate: Callable[[U, T], U], items: List[T], initial: Optional[U] = None) -> U:
'''
Reduces the list of items to a single value by applying a function cumulatively.
Args:
predicate (Callable[[U, T], U]): The function to be applied to each item in the
list with accumulator.
items (List[T]): The input list of items to be reduced.
initial (U, optional): An initial value for the reduction. If not provided, the first
item of the list is used as the initial value.
Returns:
U: The final reduced value.
'''
acc = items[0] if initial is None else initial
for item in items[1:] if initial is None else items:
acc = predicate(acc, item)
return acc
def filter(predicate: Callable[[T], bool], items: List[T]) -> List[T]:
'''
Filters the list of items to include only the items that satisfy the given predicate.
Args:
predicate (Callable[[T], bool]): The function that tests whether an item should be included.
items (List[T]): The input list of items to be filtered.
Returns:
List[T]: A new list containing only the items that satisfy the predicate.
'''
return [item for item in items if predicate(item)]
def find_index(predicate: Callable[[T], bool], items: List[T]) -> int:
'''
Finds the index of the first item in the list of items that satisfies the given predicate.
Args:
predicate (Callable[[T], bool]): The function that tests whether an item should be found.
items (List[T]): The input list of items to be searched.
Returns:
int: The index of the first item satisfying the predicate, or -1 if no such item is found.
'''
for i, item in enumerate(items):
if predicate(item):
return i
return -1
def find(predicate: Callable[[T], bool], items: List[T]) -> Optional[T]:
'''
Finds the first item in the list of items that satisfies the given predicate.
Args:
predicate (Callable[[T], bool]): The function that tests whether an item should be found.
items (List[T]): The input list of items to be searched.
Returns:
T or None: The first item that satisfies the predicate, or None if no such item is found.
'''
i = find_index(predicate, items)
return None if i < 0 else items[i]
def group_by(predicate: Callable[[T], U], items: List[T]) -> Dict[U, List[T]]:
'''
Groups items in the list of items based on the result of the predicate function.
Args:
predicate (Callable[[T], U]): The function that determines the group for each item.
items (List[T]): The input list of items to be grouped.
Returns:
Dict[U, List[T]]: A dictionary where keys are the groups and values are lists
of items in each group.
'''
groups = {}
for item in items:
group = predicate(item)
if group not in groups:
groups[group] = []
groups[group].append(item)
return groups
def for_each(predicate: Callable[[T], None], items: List[T]) -> None:
'''
Applies a given function to all items in the list of items without returning any results.
Args:
predicate (Callable[[T], None]): The function to be applied to each item in the list.
items (List[T]): The input list of items to be processed.
'''
for item in items:
predicate(item)
""" Unit tests for functional module """
from unittest.mock import Mock
import src.functional as F
def test_map():
""" Ensure that the provided function is applied to each element in the list. """
assert F.map(lambda x: x + 1, [1, 2, 3]) == [2, 3, 4]
def test_reduce():
""" Ensure that the provided function accumulates value correctly. """
assert F.reduce(lambda acc, x: acc + x, [1, 2, 3]) == 6
assert F.reduce(lambda acc, x: acc + x, [1, 2, 3], 10) == 16
def test_filter():
""" Ensure that elements are filtered based on the provided predicate. """
assert F.filter(lambda x: x % 2 == 0, [1, 2, 3, 4]) == [2, 4]
def test_find():
""" Ensure that the first element satisfying the given condition is found. """
assert F.find(lambda x: x == 2, [1, 2, 3]) == 2
assert F.find(lambda x: x == 4, [1, 2, 3]) is None
def test_find_index():
""" Ensure that the index of the first element satisfying the given condition is found. """
assert F.find_index(lambda x: x == 2, [1, 2, 3]) == 1
assert F.find_index(lambda x: x == 4, [1, 2, 3]) == -1
def test_group_by():
""" Ensure that elements are correctly grouped based on the provided predicate. """
assert F.group_by(lambda x: x % 2 == 0, [1, 2, 3, 4]) == {True: [2, 4], False: [1, 3]}
def test_for_each():
""" Ensure that the provided predicate is applied to each element in the list. """
predicate = Mock(return_value=None)
F.for_each(predicate, [1, 2, 3])
# predicate.assert_called_with(1, 2, 3, key='value')
assert predicate.call_count == 3
predicate.assert_any_call(1)
predicate.assert_any_call(2)
predicate.assert_any_call(3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment