Metadata-Version: 2.4
Name: nestfind
Version: 0.1.0
Summary: Powerful deep search for nested dict/list structures
Project-URL: Homepage, https://github.com/romysaputrasihanandaa/nestfind
Project-URL: Issues, https://github.com/romysaputrasihanandaa/nestfind/issues
License: MIT
Keywords: data,deep-search,dict,json,nested,search
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# nestfind

Powerful deep search for nested dict/list structures in Python.

Traverses arbitrarily nested `dict`/`list` data using a flexible path-based syntax,
supporting fallback paths, multiple sources, wildcard matching, predicate filtering, and more.

## Installation

```bash
pip install nestfind
```

## Quick Start

```python
from nestfind import deep_search

data = {
    "user": {
        "profile": {
            "name": "Alice",
            "email": "alice@example.com"
        }
    }
}

deep_search(data, "user", "profile", "name")   # → "Alice"
deep_search(data, "email")                      # → "alice@example.com"  (wide search)
```

## Path Segment Types

| Segment | Description | Example |
|---------|-------------|---------|
| `str` | Wide search key — finds key anywhere in nested structure | `"name"` |
| `str + "!"` | Condition key — returns the **parent dict** containing this key | `"uri!"` |
| `str + "?"` | Optional key — exact match only, skips wide search if not found | `"nickname?"` |
| `"*"` | Wildcard — matches ALL keys/items at this level | `"*"` |
| `int` | List index — exact positional access, supports negative | `0`, `-1` |
| `callable` | Predicate filter — include item only if callable returns truthy | `lambda u: u.get("active")` |

## Modes

### Single path
```python
deep_search(data, "a", "b", "c")
```

### Fallback mode — tries paths in order, returns first non-empty result
```python
deep_search(data, ["uri"], ["browser_native_hd_url"])
```

### Multi-source mode — each list is `[source, *keys]`
```python
deep_search([source1, "key1"], [source2, "key2", "key3"])
```

## Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `return_first` | `bool` | `True` | Return first match or list of all matches |
| `default` | `Any` | `None` | Value to return if nothing found |
| `type_filter` | `type` or `tuple` | `None` | Only return results of this type |
| `value_filter` | `callable` | `None` | Only return results where `value_filter(v)` is truthy |
| `transform` | `callable` | `None` | Apply function to each result before returning |
| `max_depth` | `int` | `None` | Maximum nesting depth for wide search |
| `exclude_keys` | `list[str]` | `None` | Skip these keys during wide search |
| `strict` | `bool` | `False` | Disable wide search — exact path traversal only |
| `with_path` | `bool` | `False` | Return `(value, path)` tuples instead of bare values |
| `debug` | `bool` | `False` | Enable debug logging |

## Examples

```python
from nestfind import deep_search, DeepSearch

data = {
    "users": [
        {"id": 1, "name": "Alice", "active": True},
        {"id": 2, "name": "Bob",   "active": False},
    ]
}

# Get all emails using wildcard
deep_search(data, "users", "*", "name", return_first=False)
# → ["Alice", "Bob"]

# Filter with predicate
deep_search(data, "users", lambda u: u.get("active"), "name")
# → "Alice"

# Return with path
deep_search(data, "name", with_path=True)
# → ("Alice", ["users", 0, "name"])

# Type filter
deep_search(data, "id", type_filter=int)
# → 1

# Class wrapper — bind config once, reuse
ds = DeepSearch(exclude_keys=["metadata"], max_depth=5)
ds(data, "users", "*", "name", return_first=False)
# → ["Alice", "Bob"]
```

### `DeepSearch` class

Bind configuration once and reuse across calls:

```python
class FacebookMapper:
    deep_search = DeepSearch(exclude_keys=["metadata"])

    def map(self, raw):
        return self.deep_search(raw, "user", "name")
```

## License

MIT
