Skip to content

API Reference: ListMapper

ListMapper[T] is the main eager execution class in functional_list. It wraps a Python list and provides functional-style transformations similar to Apache Spark RDD operations.

Class Signature

class ListMapper(Generic[T]):
    """Eager functional list with RDD-style operations."""

Construction

Direct Construction

from functional_list import ListMapper

# Create from individual elements
lm = ListMapper[int](1, 2, 3, 4)

# Create from unpacked iterable
items = [10, 20, 30]
lm = ListMapper[int](*items)

# Type-annotated construction
strings = ListMapper[str]("a", "b", "c")

From Files

# CSV
data = ListMapper.from_csv("file.csv", options=CSVReadOptions(...))

# JSON
data = ListMapper.from_json("file.json")

# JSONL
data = ListMapper.from_jsonl("file.jsonl")

# Text
data = ListMapper.from_text("file.txt", options=TextReadOptions(...))

# Parquet (requires pyarrow)
data = ListMapper.from_parquet("file.parquet", columns=[...])

See the I/O Guide for detailed file I/O documentation.

Core Transformation Methods

map

Apply a function to each element.

def map(self, fn: Callable[[T], U], *, backend: Optional[Backend] = None) -> ListMapper[U]

Parameters: - fn: Function to apply to each element - backend: Optional execution backend

Returns: New ListMapper with transformed elements

Example:

numbers = ListMapper[int](1, 2, 3, 4)
squared = numbers.map(lambda x: x * x)
# Result: List[1, 4, 9, 16]

filter

Keep only elements that satisfy a predicate.

def filter(self, fn: Callable[[T], bool], *, backend: Optional[Backend] = None) -> ListMapper[T]

Parameters: - fn: Predicate function (returns True to keep element) - backend: Optional execution backend

Returns: New ListMapper with filtered elements

Example:

numbers = ListMapper[int](1, 2, 3, 4, 5, 6)
evens = numbers.filter(lambda x: x % 2 == 0)
# Result: List[2, 4, 6]

flat_map

Map each element to multiple elements and flatten.

def flat_map(self, fn: Callable[[T], Iterable[U]], *, backend: Optional[Backend] = None) -> ListMapper[U]

Parameters: - fn: Function that returns an iterable for each element - backend: Optional execution backend

Returns: New ListMapper with flattened results

Example:

sentences = ListMapper[str]("hello world", "python rocks")
words = sentences.flat_map(lambda s: s.split())
# Result: List['hello', 'world', 'python', 'rocks']

reduce

Reduce all elements to a single value.

def reduce(self, fn: Callable[[T, T], T], *, backend: Optional[Backend] = None) -> T

Parameters: - fn: Binary reduction function - backend: Optional execution backend

Returns: Single reduced value

Example:

numbers = ListMapper[int](1, 2, 3, 4, 5)
total = numbers.reduce(lambda x, y: x + y)
# Result: 15

foreach

Execute a function for each element (for side effects).

def foreach(self, fn: Callable[[T], Any], *, backend: Optional[Backend] = None) -> None

Parameters: - fn: Function to execute (return value ignored) - backend: Optional execution backend

Returns: None

Example:

numbers = ListMapper[int](1, 2, 3)
numbers.foreach(lambda x: print(f"Value: {x}"))
# Prints:
# Value: 1
# Value: 2
# Value: 3

Key-Value Pair Methods

These methods work on ListMapper containing tuples (key, value).

group_by_key

Group values by their keys.

def group_by_key(self) -> ListMapper[Tuple[K, List[V]]]

Returns: ListMapper of (key, [values]) tuples

Example:

pairs = ListMapper(("a", 1), ("b", 2), ("a", 3), ("b", 4))
grouped = pairs.group_by_key()
# Result: List[('a', [1, 3]), ('b', [2, 4])]

reduce_by_key

Reduce values for each key.

def reduce_by_key(self, fn: Callable[[V, V], V]) -> ListMapper[Tuple[K, V]]

Parameters: - fn: Binary reduction function for values

Returns: ListMapper of (key, reduced_value) tuples

Example:

pairs = ListMapper(("a", 1), ("b", 2), ("a", 3), ("b", 4))
sums = pairs.reduce_by_key(lambda x, y: x + y)
# Result: List[('a', 4), ('b', 6)]

group_by

Group elements by a key function.

def group_by(self, fn: Callable[[T], K]) -> ListMapper[Tuple[K, List[T]]]

Parameters: - fn: Function to extract grouping key

Returns: ListMapper of (key, [elements]) tuples

Example:

numbers = ListMapper[int](1, 2, 3, 4, 5, 6)
by_parity = numbers.group_by(lambda x: "even" if x % 2 == 0 else "odd")
# Result: List[('odd', [1, 3, 5]), ('even', [2, 4, 6])]

Sorting and Ordering

sort

Sort elements.

def sort(self, *, key: Optional[Callable[[T], Any]] = None, reverse: bool = False) -> ListMapper[T]

Parameters: - key: Optional function to extract comparison key - reverse: Sort in descending order if True

Returns: New sorted ListMapper

Example:

numbers = ListMapper[int](3, 1, 4, 1, 5, 9)
sorted_asc = numbers.sort()
# Result: List[1, 1, 3, 4, 5, 9]

sorted_desc = numbers.sort(reverse=True)
# Result: List[9, 5, 4, 3, 1, 1]

words = ListMapper[str]("apple", "pie", "a", "cherry")
by_length = words.sort(key=lambda s: len(s))
# Result: List['a', 'pie', 'apple', 'cherry']

Aggregation and Statistics

count

Count the number of elements.

def count(self) -> int

Example:

numbers = ListMapper[int](1, 2, 3, 4, 5)
n = numbers.count()  # 5

sum

Sum all numeric elements.

def sum(self) -> T

Example:

numbers = ListMapper[int](1, 2, 3, 4, 5)
total = numbers.sum()  # 15

mean

Calculate the arithmetic mean.

def mean(self) -> float

Example:

numbers = ListMapper[int](1, 2, 3, 4, 5)
avg = numbers.mean()  # 3.0

min / max

Find minimum or maximum element.

def min(self) -> T
def max(self) -> T

Example:

numbers = ListMapper[int](3, 1, 4, 1, 5)
smallest = numbers.min()  # 1
largest = numbers.max()   # 5

Set Operations

distinct

Remove duplicate elements, preserving first occurrence order.

def distinct(self, *, backend: Optional[BaseBackend] = None) -> ListMapper[T]

Parameters: - backend: Optional execution backend

Returns: New ListMapper with unique elements

Example:

numbers = ListMapper[int](1, 2, 2, 3, 3, 3, 4)
unique = numbers.distinct()
# Result: List[1, 2, 3, 4]

# Preserves order of first occurrence
ordered = ListMapper[int](3, 1, 4, 1, 5, 9, 2, 6, 5, 3)
unique = ordered.distinct()
# Result: List[3, 1, 4, 5, 9, 2, 6]

# With backend
from functional_list.backend import LocalBackend
unique = numbers.distinct(backend=LocalBackend(mode="threads"))

sort

Sort elements using an optional key function.

def sort(
    self,
    *,
    key: Optional[Callable[[T], Any]] = None,
    reverse: bool = False,
    backend: Optional[BaseBackend] = None,
) -> ListMapper[T]

Parameters: - key: Optional function to extract comparison key from each element - reverse: If True, sort in descending order (default: False) - backend: Optional execution backend

Returns: New ListMapper with sorted elements

Important Notes: - Sort is stable (maintains relative order of equal elements) - Returns a new ListMapper (immutable operation) - Uses Python's optimized Timsort algorithm - LocalBackend in serial mode uses optional Cython acceleration

Examples:

# Sort numbers in ascending order
numbers = ListMapper(3, 1, 4, 1, 5, 9, 2, 6)
sorted_asc = numbers.sort()
# Result: List[1, 1, 2, 3, 4, 5, 6, 9]

# Sort in descending order
sorted_desc = numbers.sort(reverse=True)
# Result: List[9, 6, 5, 4, 3, 2, 1, 1]

# Sort with key function
words = ListMapper("apple", "pie", "a", "cherry")
by_length = words.sort(key=lambda x: len(x))
# Result: List['a', 'pie', 'apple', 'cherry']

# Sort dictionaries by field
users = ListMapper(
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 35}
)
by_age = users.sort(key=lambda u: u["age"])
# Result: List[{'name': 'Bob', 'age': 25}, ...]

# Sort by multiple criteria
data = ListMapper(
    {"category": "A", "value": 2},
    {"category": "B", "value": 1},
    {"category": "A", "value": 1}
)
sorted_data = data.sort(key=lambda x: (x["category"], x["value"]))
# Result: Sorted by category, then by value

# Sort with backend
from functional_list.backend import LocalBackend
sorted_nums = numbers.sort(backend=LocalBackend(mode="serial"))

Performance

When using LocalBackend(mode="serial"), the sort operation can use optional Cython acceleration for improved performance on large datasets.

Chaining

Sort integrates seamlessly with method chaining:

result = (
    ListMapper(*range(20))
    .filter(lambda x: x % 2 == 0)
    .map(lambda x: x * 2)
    .sort(reverse=True)
)

union

Combine two ListMapper objects of the same type into one.

def union(self, other: ListMapper[T], *, backend: Optional[BaseBackend] = None) -> ListMapper[T]

Parameters: - other: Another ListMapper to union with - backend: Optional execution backend (reserved for future optimization)

Returns: New ListMapper containing all elements from both ListMappers

Raises: - TypeError: If other is not a ListMapper instance - TypeError: If the ListMappers contain incompatible types

Important Notes: - Elements from self appear first, followed by elements from other - Does not remove duplicates - use .union(other).distinct() for that - Returns a new ListMapper (immutable operation) - Performs runtime type checking to prevent incompatible unions

Examples:

# Basic union with integers
list1 = ListMapper[int](1, 2, 3)
list2 = ListMapper[int](4, 5, 6)
result = list1.union(list2)
# Result: List[1, 2, 3, 4, 5, 6]

# Union with dictionaries
dict1 = ListMapper({'a': 1}, {'b': 2})
dict2 = ListMapper({'c': 3}, {'d': 4})
result = dict1.union(dict2)
# Result: List[{'a': 1}, {'b': 2}, {'c': 3}, {'d': 4}]

# Union preserves duplicates
list1 = ListMapper[int](1, 2, 3)
list2 = ListMapper[int](3, 4, 5)
result = list1.union(list2)
# Result: List[1, 2, 3, 3, 4, 5]

# Remove duplicates after union
result = list1.union(list2).distinct()
# Result: List[1, 2, 3, 4, 5]

# Type checking - this raises TypeError
int_list = ListMapper[int](1, 2, 3)
str_list = ListMapper[str]("a", "b", "c")
int_list.union(str_list)  # TypeError: incompatible types

# With backend (for future optimization)
from functional_list.backend import LocalBackend
result = list1.union(list2, backend=LocalBackend(mode="threads"))

Type Safety

The union method performs runtime type checking. Attempting to union ListMappers with incompatible types (e.g., ListMapper[int] with ListMapper[str]) will raise a TypeError.

Chaining Operations

Union works seamlessly with method chaining:

result = (
    ListMapper(1, 2, 3)
    .union(ListMapper(4, 5, 6))
    .map(lambda x: x * 2)
    .filter(lambda x: x > 5)
)
# Result: List[6, 8, 10, 12]

intersection

Get elements present in both ListMappers.

def intersection(self, other: ListMapper[T]) -> ListMapper[T]

Example:

a = ListMapper[int](1, 2, 3, 4)
b = ListMapper[int](3, 4, 5, 6)
common = a.intersection(b)
# Result: List[3, 4]

Sampling and Partitioning

take

Get first n elements.

def take(self, n: int) -> ListMapper[T]

Example:

numbers = ListMapper[int](*range(100))
first_5 = numbers.take(5)
# Result: List[0, 1, 2, 3, 4]

sample

Get random sample of n elements.

def sample(self, n: int, *, seed: Optional[int] = None) -> ListMapper[T]

Parameters: - n: Number of elements to sample - seed: Optional random seed for reproducibility

Example:

numbers = ListMapper[int](*range(100))
random_10 = numbers.sample(10, seed=42)

Lazy Evaluation

lazy

Convert to lazy evaluation mode.

def lazy(self) -> LazyListMapper[T]

Returns: LazyListMapper that defers computation

Example:

lazy_pipeline = (
    ListMapper[int](1, 2, 3, 4)
    .lazy()
    .map(lambda x: x * x)
    .filter(lambda x: x > 5)
)
result = lazy_pipeline.collect()  # Execute and return ListMapper

See LazyListMapper API for lazy-specific methods.

Conversion Methods

to_list

Convert to Python list.

def to_list(self) -> List[T]

Example:

lm = ListMapper[int](1, 2, 3)
py_list = lm.to_list()  # [1, 2, 3]

to_json

Write to JSON file.

def to_json(self, path: str) -> None

Example:

data = ListMapper[dict]({"id": 1}, {"id": 2})
data.to_json("output.json")

to_jsonl

Write to JSONL file.

def to_jsonl(self, path: str) -> None

Example:

logs = ListMapper[dict]({"msg": "Started"}, {"msg": "Completed"})
logs.to_jsonl("events.log")

List Compatibility

ListMapper supports standard Python list operations:

Indexing and Slicing

lm = ListMapper[int](10, 20, 30, 40, 50)

# Indexing
first = lm[0]      # 10
last = lm[-1]      # 50

# Slicing
middle = lm[1:4]   # List[20, 30, 40]
every_other = lm[::2]  # List[10, 30, 50]

Length

lm = ListMapper[int](1, 2, 3)
n = len(lm)  # 3

Iteration

lm = ListMapper[int](1, 2, 3)
for item in lm:
    print(item)

Membership

lm = ListMapper[int](1, 2, 3)
has_2 = 2 in lm  # True
has_5 = 5 in lm  # False

Append and Extend

lm = ListMapper[int](1, 2, 3)
lm.append(4)        # List[1, 2, 3, 4]
lm.extend([5, 6])   # List[1, 2, 3, 4, 5, 6]

Backend Support

All transformation methods (map, filter, foreach, reduce, flat_map) accept an optional backend parameter:

from functional_list import ListMapper, LocalBackend, RayBackend

lm = ListMapper[int](*range(1000))

# Use threading
result = lm.map(fn, backend=LocalBackend(mode="threads", workers=4))

# Use multiprocessing
result = lm.map(fn, backend=LocalBackend(mode="processes", workers=4))

# Use Ray (if installed)
result = lm.map(fn, backend=RayBackend(num_cpus=8))

See the Backends Guide for detailed information.

Global Backend Configuration

set_default_backend

Set default backend for all operations.

@classmethod
def set_default_backend(cls, backend: Backend) -> None

Example:

from functional_list import ListMapper, LocalBackend

ListMapper.set_default_backend(LocalBackend(mode="threads", workers=8))

# Now all operations use threading by default
result = ListMapper[int](1, 2, 3).map(lambda x: x * 2)

using_backend

Temporary backend context manager.

@classmethod
@contextmanager
def using_backend(cls, backend: Backend)

Example:

from functional_list import ListMapper, LocalBackend

with ListMapper.using_backend(LocalBackend(mode="threads")):
    result = ListMapper[int](1, 2, 3).map(lambda x: x * 2)
    # Uses threading

# Outside context, uses default backend

Type Safety

ListMapper is fully typed and works with type checkers:

from functional_list import ListMapper

# Type checker knows this is ListMapper[int]
numbers: ListMapper[int] = ListMapper[int](1, 2, 3)

# Type checker knows this is ListMapper[str]
strings: ListMapper[str] = numbers.map(str)

# Type error: incompatible types
# numbers.map(lambda x: "str")  # OK
# strings.map(lambda x: x + 1)  # Type error!

See Also