Skip to content

Instantly share code, notes, and snippets.

@erikccoder
Last active June 24, 2025 08:40
Show Gist options
  • Select an option

  • Save erikccoder/249dff50845500f822c62f582a0a210a to your computer and use it in GitHub Desktop.

Select an option

Save erikccoder/249dff50845500f822c62f582a0a210a to your computer and use it in GitHub Desktop.
python-lodash-get-lambda

Python Lodash _.get() Lambda Implementation

This gist provides a Python equivalent of Lodash's _.get() function implemented as a lambda expression. The function safely accesses nested object properties using a dot-separated string or array path, returning a default value if the path is invalid.

Code

get=lambda obj,path,default=None:(lambda x:default if x is None else x)((lambda p:next(((obj[int(A[0])]if A[0].isdigit()and isinstance(obj,list)else obj.get(A[0],default))if len(A)==1 else get(obj[int(A[0])]if A[0].isdigit()and isinstance(obj,list)else obj.get(A[0],{}),A[1:],default)for A in[path.split('.')if isinstance(path,str)else path]),default))(path))

Usage

The get function takes three parameters:

  • obj: The object to query (e.g., a nested dictionary).
  • path: The path to the property, either as a dot-separated string (e.g., 'a.b.c') or an array (e.g., ['a', 'b', 'c']).
  • default: The value to return if the path is invalid or the property is None (defaults to None).

Example

obj = {
    'a': {
        'b': {
            'c': 123
        }
    }
}

# String path
print(get(obj, 'a.b.c'))  # Output: 123
print(get(obj, 'a.b.d', 'not found'))  # Output: 'not found'

# Array path
print(get(obj, ['a', 'b', 'c']))  # Output: 123
print(get(obj, ['a', 'b', 'd'], 'not found'))  # Output: 'not found'

# Non-existent path
print(get(obj, 'x.y.z', 'not found'))  # Output: 'not found'

How It Works

  1. Path Handling: Converts a string path (e.g., 'a.b.c') to a list by splitting on dots, or uses the provided list path directly.
  2. Recursive Access: Uses dict.get() to safely access each path segment:
    • If only one segment remains, returns the value or the default.
    • If multiple segments remain, recursively calls get on the nested object (or an empty dict if the key is missing).
  3. Default Value: Ensures the final result returns the default value if the resolved value is None.

Limitations

  • Basic Functionality: This is a simplified implementation compared to Lodash's _.get(). It assumes the object is a dictionary and doesn't handle advanced cases like:
    • Array indices (e.g., a[0].b).
    • Special characters in keys.
    • Non-dictionary objects in the path.
  • Performance: The lambda-based approach may be less performant for deeply nested objects compared to a traditional function.
  • Edge Cases: Does not handle all edge cases as robustly as the full Lodash library.

For production use, consider using a library like python-lodash or a more robust recursive function.

Why This Implementation?

This code was created to replicate Lodash's _.get() behavior in Python using a concise lambda expression, avoiding unsafe methods like eval(). It fixes issues like KeyError by using dict.get() and provides a functional, single-line solution.

License

This code is provided under the MIT License. Feel free to use, modify, and distribute it as needed.

This implementation was created by Grok, built by xAI.

import time
import random
import string
import psutil
from pydash import get as pydash_get
# Fixed lambda-based _.get() implementation
get = lambda obj, path, default=None: (lambda x: default if x is None else x)(
(lambda p: next(
(
(obj[int(p[0])] if p[0].isdigit() and isinstance(obj, list) else obj.get(p[0], default))
if len(p) == 1 else
get(
obj[int(p[0])] if p[0].isdigit() and isinstance(obj, list) else obj.get(p[0], {}),
p[1:],
default
)
for p in [(path.split('.') if isinstance(path, str) else path)]
),
default
))(path)
)
# Function to generate a deeply nested dictionary
def generate_nested_dict(depth, width=2):
if depth == 0:
return random.randint(1, 1000)
return {f'key_{i}': generate_nested_dict(depth - 1, width) for i in range(width)}
# Function to generate a large flat dictionary
def generate_large_dict(size):
return {f'key_{i}': random.randint(1, 1000) for i in range(size)}
# Function to generate random paths
def generate_random_path(max_depth, use_array=False):
length = random.randint(1, max_depth)
if use_array:
return [''.join(random.choices(string.ascii_lowercase, k=5)) for _ in range(length)]
return '.'.join(''.join(random.choices(string.ascii_lowercase, k=5)) for _ in range(length))
# Function to measure memory usage
def get_memory_usage():
process = psutil.Process()
return process.memory_info().rss / 1024 / 1024 # Memory in MB
# Stress test function
def stress_test(test_name, obj, paths, iterations=1000):
print(f"\n=== {test_name} ===")
lambda_times, pydash_times = [], []
lambda_memory, pydash_memory = [], []
lambda_correct, pydash_correct = 0, 0
# Expected results (using pydash as reference for correctness)
expected_results = [pydash_get(obj, path, 'not found') for path in paths]
for i in range(iterations):
# Shuffle paths to randomize access order
random.shuffle(paths)
# Test lambda get
start_time = time.perf_counter()
start_memory = get_memory_usage()
for j, path in enumerate(paths):
result = get(obj, path, 'not found')
if result == expected_results[j]:
lambda_correct += 1
lambda_time = time.perf_counter() - start_time
lambda_memory.append(get_memory_usage() - start_memory)
lambda_times.append(lambda_time)
# Test pydash get
start_time = time.perf_counter()
start_memory = get_memory_usage()
for j, path in enumerate(paths):
result = pydash_get(obj, path, 'not found')
if result == expected_results[j]:
pydash_correct += 1
pydash_time = time.perf_counter() - start_time
pydash_memory.append(get_memory_usage() - start_memory)
pydash_times.append(pydash_time)
# Report results
print(f"Lambda - Avg Time: {sum(lambda_times) / iterations:.6f}s, Avg Memory: {sum(lambda_memory) / iterations:.2f}MB, Correctness: {lambda_correct / (iterations * len(paths)) * 100:.2f}%")
print(f"Pydash - Avg Time: {sum(pydash_times) / iterations:.6f}s, Avg Memory: {sum(pydash_memory) / iterations:.2f}MB, Correctness: {pydash_correct / (iterations * len(paths)) * 100:.2f}%")
# Test scenarios
def run_stress_tests():
# Scenario 1: Deeply nested dictionary
deep_obj = generate_nested_dict(depth=10)
deep_paths = [generate_random_path(12, use_array=False) for _ in range(100)] # Some invalid paths
stress_test("Deep Nesting", deep_obj, deep_paths)
# Scenario 2: Large flat dictionary
large_obj = generate_large_dict(10000)
large_paths = [f'key_{random.randint(0, 15000)}' for _ in range(100)] # Some invalid keys
stress_test("Large Dictionary", large_obj, large_paths)
# Scenario 3: Mixed paths (string and array)
mixed_obj = generate_nested_dict(depth=5)
mixed_paths = [generate_random_path(7, use_array=(i % 2 == 0)) for i in range(100)]
stress_test("Mixed Paths", mixed_obj, mixed_paths)
# Scenario 4: Edge cases (updated to include list index test)
edge_obj = {'a': None, 'b': {}, 'c': {'d': [1, 2, 3]}}
edge_paths = ['a', 'b.x', 'c.d.0', 'x.y.z', '']
stress_test("Edge Cases", edge_obj, edge_paths)
# Run the tests
if __name__ == '__main__':
run_stress_tests()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment