Skip to main content

A lazy selector for nested Python data structures.

Project description

dictselect

A Python library for extracting data from nested dicts and lists using reusable pipelines.

from dictselect import Selector

pipe = Selector["annotations"][:]["x_min", "x_max"]
my_data_selection = pipe(data_dict)

Installation

pip install dictselect

Requires Python ≥ 3.9.

How it works

Build a Selector by chaining operations, then call it with your data. The pipeline is built to be reusable.

from dictselect import Selector

data_dict = {
    "image_id": "xa001",
    "annotations": [
        {"id": 1, "x_min": 10, "x_max": 20, "label": "cat"},
        {"id": 2, "x_min": 30, "x_max": 50, "label": "dog"},
    ],
}

Selector["image_id"](data_dict)                          # → "xa001"
Selector["annotations"][0]["label"](data_dict)           # → "cat"
Selector["annotations"][:]["label"](data_dict)           # → ["cat", "dog"]
Selector["annotations"][:]["x_min", "x_max"](data_dict)  # → [[10, 20], [30, 50]]

Operations

Syntax What it does
Selector["key"] Dict key or list index lookup
Selector[0], Selector[-1] List index
Selector[1:3] Slice
Selector[:] or Selector[...] Fan-out — apply the rest of the chain to every element at this step
Selector["a", "b"] Input multiple keys at once, returns a list
Selector.method() Call a method on the current value
pipe_a + pipe_b Compose two pipelines into one
Selector.invoke(*args, **kwargs) Call the function if the current value is a function

Fan-out [:]

[:] maps the remaining steps over every item in this step. Steps after [:] run on each element individually.

data = [{"v": 1}, {"v": 2}, {"v": 3}]

Selector[:]["v"](data)   # → [1, 2, 3]
Selector[:][:][0]([[10, 20], [30, 40]])  # → [[10], [30]]  (nested fan-out)

Multi-key input ["a", "b"]

Returns a list of values for each key. All keys must be the same type (all strings or all integers).

Selector["x", "y"]({"x": 1, "y": 2, "z": 3})  # → [1, 2]

Method calls

Access an attribute, then call it like a regular Python method.

Selector.upper()("hello")               # → "HELLO"
Selector[:].upper()(["hi", "there"])    # → ["HI", "THERE"]

Composition

Join two pipelines with +.

head = Selector["data"][:]
tail = Selector["value"]
(head + tail)({"data": [{"value": 1}, {"value": 2}]})  # → [1, 2]

Including keys in the result

Pass include_keys=True to wrap the result with the last key as a dict. Works for single key lookups and multi-key inputs.

Selector["a"]["b"]({"a": {"b": 12}}, include_keys=True)
# → {"b": 12}

Selector[:]["a"]([{"a": 1}, {"a": 2}], include_keys=True)
# → [{"a": 1}, {"a": 2}]

Selector[:]["a", "b"]([{"a": 1, "b": 2, "c": 3}, {"a": 4, "c": 6, "b": 5}], include_keys=True)
# → [{"a": 1, "b": 2}, {"a": 4, "b": 5}]

Also works on .apply():

Selector["x"].apply({"x": 7}, include_keys=True)  # → {"x": 7}

Handling missing values

Pass include_null=True to get None instead of a KeyError/IndexError when a key or index doesn't exist. Once a step fails, the rest of the chain is skipped and None is returned.

Selector["a"]["missing"]({"a": {}}, include_null=True)
# → None   (instead of KeyError)

Selector[:]["x"]([{"x": 1}, {"y": 2}, {"x": 3}], include_null=True)
# → [1, None, 3]

Selector["a", "b"]({"a": 1}, include_null=True)
# → [1, None]   (missing keys in multi-select become None individually)

The two flags can be combined:

Selector[:]["x"]([{"x": 1}, {"y": 2}], include_null=True, include_keys=True)
# → [{"x": 1}, {"x": None}]

Calling vs. evaluating

Normally, calling a selector evaluates it:

pipe = Selector["key"]
pipe({"key": 42})  # → 42

Exception 1: if the last step is an attribute name (e.g. .upper), calling it records a method call instead of evaluating. Use .apply(data) to force evaluation in that case.

pipe = Selector["title"].upper()       # records .upper() call
pipe({"title": "hello"})               # evaluates → "HELLO"

Selector.upper.apply("hello")          # force evaluation → <method object>

Exception 2: if the last step is a function as a value, use .invoke(*args, **kwargs) to force evaluation in that case.

pipe = Selector["function"]()            # value will be a function. Calling the function, results in evaluating the selector -> ERROR.
pipe = Selector["function"].invoke()     # Calls the function without evaluating the Selector

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dictselect-0.1.0.tar.gz (11.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dictselect-0.1.0-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file dictselect-0.1.0.tar.gz.

File metadata

  • Download URL: dictselect-0.1.0.tar.gz
  • Upload date:
  • Size: 11.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.12

File hashes

Hashes for dictselect-0.1.0.tar.gz
Algorithm Hash digest
SHA256 36725b399ed1f8e6c8d9f68aa0c7601bbb4aada75766270bc7180fb9a4d9dd34
MD5 a3639fb4704744896fd143440f3762e5
BLAKE2b-256 b5b07cfc200c61266eb23a13706726b215af56ca5aa35b24b0a5130f9d9135be

See more details on using hashes here.

File details

Details for the file dictselect-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: dictselect-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.12

File hashes

Hashes for dictselect-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5752042f1c68ef2dae566fde04652720c0e3220d2461d23c025125e3839f61fd
MD5 4512d582871f094af76a319adbeba1e6
BLAKE2b-256 67939ec2789a23c6cb56511317940529fab9fcc9458055bc7185b3e6acbbd6d2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page