A comprehensive Python utility library for functional programming inspired by JavaScript's Lo-Dash
—
The Chaining module provides method chaining functionality that enables fluent interface patterns for composing multiple pydash operations in a readable, left-to-right manner. This module consists of 3 main components plus the chaining class.
def chain(value: Any = None) -> _DashCreates a _Dash object which wraps value to enable method chaining.
Parameters:
value (Any, optional): Value to wrap for chaining.Returns:
_Dash: Chaining wrapper object.Example:
from pydash import chain
result = chain([1, 2, 3, 4, 5, 6])\
.filter(lambda x: x % 2 == 0)\
.map(lambda x: x * 2)\
.value()
# [4, 8, 12]
# Complex data processing
data = [
{'name': 'John', 'age': 30, 'salary': 50000},
{'name': 'Jane', 'age': 25, 'salary': 60000},
{'name': 'Bob', 'age': 35, 'salary': 45000}
]
result = chain(data)\
.filter(lambda x: x['age'] > 25)\
.sort_by('salary')\
.map('name')\
.value()
# ['Bob', 'John']def _(value: Any = None) -> _DashShorthand alias for the chain function. Creates a _Dash object for method chaining.
Parameters:
value (Any, optional): Value to wrap for chaining.Returns:
_Dash: Chaining wrapper object.Example:
from pydash import _
# Same functionality as chain()
result = _([1, 2, 3, 4, 5])\
.filter(lambda x: x > 2)\
.map(lambda x: x ** 2)\
.sum_()\
.value()
# 50 (3² + 4² + 5² = 9 + 16 + 25)def tap(value: Any, interceptor: Callable) -> AnyInvokes interceptor with value and returns value. This method is useful for debugging intermediate values in a chain.
Parameters:
value (Any): Value to pass to interceptor and return.interceptor (Callable): Function to invoke with value.Returns:
Any: Returns value.Example:
from pydash import tap, _
# Debug intermediate results in a chain
result = _([1, 2, 3, 4, 5])\
.filter(lambda x: x % 2 == 1)\
.tap(lambda x: print(f"After filter: {x}"))\
.map(lambda x: x ** 2)\
.tap(lambda x: print(f"After map: {x}"))\
.value()
# Output: After filter: [1, 3, 5]
# After map: [1, 9, 25]
# Result: [1, 9, 25]
# Using tap for side effects
def log_progress(data):
print(f"Processing {len(data)} items")
return data
result = _(data)\
.filter(condition)\
.tap(log_progress)\
.map(transform)\
.value()class _Dash:
def __init__(self, value: Any = None)The main chaining class that wraps values to enable method chaining. All pydash functions are available as methods on _Dash instances.
Parameters:
value (Any, optional): Initial value to wrap.Key Methods:
.value(): Unwraps and returns the final resultExample:
from pydash import _
# Create a _Dash instance
wrapper = _([1, 2, 3, 4, 5])
# Chain operations
result = wrapper\
.filter(lambda x: x > 2)\
.map(lambda x: x * 2)\
.reverse()\
.value()
# [10, 8, 6]from pydash import _
# Simple transformation chain
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = _(numbers)\
.filter(lambda x: x % 2 == 0)\
.map(lambda x: x ** 2)\
.take(3)\
.value()
# [4, 16, 36]from pydash import _
users = [
{'name': 'John', 'age': 30, 'active': True, 'department': 'Engineering'},
{'name': 'Jane', 'age': 25, 'active': True, 'department': 'Marketing'},
{'name': 'Bob', 'age': 35, 'active': False, 'department': 'Engineering'},
{'name': 'Alice', 'age': 28, 'active': True, 'department': 'Sales'}
]
# Complex object processing
active_eng_names = _(users)\
.filter({'active': True})\
.filter(lambda u: u['department'] == 'Engineering')\
.map('name')\
.value()
# ['John']
# Grouping and aggregation
dept_summary = _(users)\
.filter({'active': True})\
.group_by('department')\
.map_values(lambda group: {\
'count': len(group),\
'avg_age': sum(u['age'] for u in group) / len(group)\
})\
.value()from pydash import _
text = " Hello, World! This is a test. "
processed = _(text)\
.trim()\
.to_lower()\
.replace('!', '')\
.split(' ')\
.filter(lambda x: len(x) > 2)\
.map(lambda x: x.capitalize())\
.join(' ')\
.value()
# "Hello World This Test"from pydash import _
data = [
{'product': 'A', 'sales': 100, 'region': 'North'},
{'product': 'B', 'sales': 150, 'region': 'South'},
{'product': 'A', 'sales': 120, 'region': 'South'},
{'product': 'C', 'sales': 200, 'region': 'North'},
{'product': 'B', 'sales': 80, 'region': 'North'}
]
# Calculate total sales by product
product_totals = _(data)\
.group_by('product')\
.map_values(lambda group: sum(item['sales'] for item in group))\
.to_pairs()\
.sort_by(lambda x: x[1])\
.reverse()\
.value()
# [('C', 200), ('B', 230), ('A', 220)]
# Statistical summary
stats = _(data)\
.map('sales')\
.thru(lambda sales: {\
'total': _(sales).sum_().value(),\
'average': _(sales).mean().value(),\
'max': _(sales).max_().value(),\
'min': _(sales).min_().value()\
})\
.value()from pydash import _
def debug_print(label):
def printer(data):
print(f"{label}: {data}")
return data
return printer
result = _([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\
.tap(debug_print("Initial"))\
.filter(lambda x: x % 2 == 0)\
.tap(debug_print("After filter"))\
.map(lambda x: x ** 2)\
.tap(debug_print("After map"))\
.take(3)\
.tap(debug_print("After take"))\
.value()
# Output:
# Initial: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# After filter: [2, 4, 6, 8, 10]
# After map: [4, 16, 36, 64, 100]
# After take: [4, 16, 36]from pydash import _
def conditional_process(data, include_filter=True, sort_desc=False):
chain_obj = _(data)
# Conditional filtering
if include_filter:
chain_obj = chain_obj.filter(lambda x: x > 0)
# Conditional sorting
if sort_desc:
chain_obj = chain_obj.sort().reverse()
else:
chain_obj = chain_obj.sort()
return chain_obj.value()
data = [3, -1, 4, -2, 5, 0]
result1 = conditional_process(data, True, True) # [5, 4, 3] (filtered & desc)
result2 = conditional_process(data, False, False) # [-2, -1, 0, 3, 4, 5] (all & asc)All pydash functions are available as methods on _Dash instances:
_([1, 2, 3, 4]).chunk(2).flatten().uniq().compact().value()_(users).filter({'active': True}).map('name').sort_by().value()_(obj).pick('name', 'age').merge(other_obj).value()_('hello world').camel_case().capitalize().value()_([1, 2, 3, 4, 5]).mean().value()_(func).once().debounce(100).value()_(value).is_string().value() # Note: predicates return boolean, not chainable_(data).default_to('fallback').times(3).value()Chaining creates intermediate wrapper objects, so for simple operations, direct function calls may be more efficient:
# For simple operations, direct calls may be better
from pydash import map_, filter_
# Direct (more efficient for simple cases)
result = map_(filter_([1, 2, 3, 4], lambda x: x > 2), lambda x: x * 2)
# Chained (better for complex operations)
result = _([1, 2, 3, 4]).filter(lambda x: x > 2).map(lambda x: x * 2).value()from pydash import _, attempt
def safe_chain(data):
try:
return _(data)\
.filter(lambda x: x is not None)\
.map(lambda x: int(x) if isinstance(x, str) else x)\
.filter(lambda x: x > 0)\
.value()
except Exception as e:
print(f"Chain processing failed: {e}")
return []
# Using attempt for individual operations
result = _(data)\
.map(lambda x: attempt(int, x))\
.filter(lambda x: not isinstance(x, Exception))\
.value()The Chaining module enables fluent, readable data processing pipelines by allowing all pydash functions to be chained together in a natural left-to-right flow. This approach is particularly powerful for complex data transformations, filtering operations, and functional programming patterns.
Install with Tessl CLI
npx tessl i tessl/pypi-pydash