{"id":28429572,"url":"https://github.com/mkrd/pathdict","last_synced_at":"2025-07-04T18:30:27.818Z","repository":{"id":40322460,"uuid":"266129604","full_name":"mkrd/PathDict","owner":"mkrd","description":"Easily query and modify Python dicts!","archived":false,"fork":false,"pushed_at":"2024-06-18T07:54:42.000Z","size":395,"stargazers_count":25,"open_issues_count":6,"forks_count":6,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-05T13:47:56.491Z","etag":null,"topics":["data","data-analysis","data-mining","data-science","data-structure","data-structures","datascience","dataset","dict","dictionaries","dictionary","json","modify","object","python","python-list","query","query-builder","science"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mkrd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-22T14:23:55.000Z","updated_at":"2024-08-05T08:46:57.000Z","dependencies_parsed_at":"2023-11-12T16:09:40.592Z","dependency_job_id":"e0c9fd0b-f4e1-43c2-b8a3-92dbadf1b710","html_url":"https://github.com/mkrd/PathDict","commit_stats":{"total_commits":217,"total_committers":4,"mean_commits":54.25,"dds":"0.18433179723502302","last_synced_commit":"38410053c40417edd57cd4274a1c5317ed8957b0"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/mkrd/PathDict","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkrd%2FPathDict","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkrd%2FPathDict/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkrd%2FPathDict/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkrd%2FPathDict/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mkrd","download_url":"https://codeload.github.com/mkrd/PathDict/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkrd%2FPathDict/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263596528,"owners_count":23486163,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data","data-analysis","data-mining","data-science","data-structure","data-structures","datascience","dataset","dict","dictionaries","dictionary","json","modify","object","python","python-list","query","query-builder","science"],"created_at":"2025-06-05T13:38:27.551Z","updated_at":"2025-07-04T18:30:27.806Z","avatar_url":"https://github.com/mkrd.png","language":"Python","readme":"\n![Logo](https://github.com/mkrd/PathDict/blob/main/assets/logo.png?raw=true)\n\n[![Total Downloads](https://pepy.tech/badge/path-dict)](https://pepy.tech/project/path-dict)\n![Tests](https://github.com/mkrd/PathDict/actions/workflows/test.yml/badge.svg)\n![Coverage](https://github.com/mkrd/PathDict/blob/main/assets/coverage.svg?raw=1)\n\n\nWhy do I need this?\n================================================================================\n\nDo you work with dicts a lot, but you also wish that they could do more?\nThen PathDict is for you!\n\nLets look at this dict:\n\n```python\nusers = {\n    \"u1\": {\n        \"name\": \"Julia\",\n        \"age\": 32,\n        \"interests\": [\"soccer\", \"basketball\"],\n    },\n    \"u2\": {\n        \"name\": \"Ben\",\n        \"age\": 26,\n        \"interests\": [\"airplanes\", \"alternative music\"],\n    }\n}\n```\n\nWith PathDict, you can do things like:\n\n```python\nusers = PathDict(users)\n\n# Get all user names\nusers[\"*\", \"name\"]  # -\u003e [\"Julia\", \"Ben\"]\n\n# Add new post to the current_user's posts\nnew_post = {\"title\": ...}\nusers[current_user.id, \"posts\"] = lambda x: (x or []) + [new_post]  # Key \"posts\" is automatically created!\n\n\n# Increase age of Julia\nusers[\"u1\", \"age\"] = 33\n\n# Append interest \"cooking\" to all users\nusers[\"*\", \"interests\"] = lambda interests: interests + [\"cooking\"]\n\n\n# Remove all interests of Ben which do not start with \"a\" (\"cooking is removed\")\nusers.filter(\"u2\", \"interests\", f=lambda interest: not interest.startswith(\"a\"))\n\n# Remove users that are younger than 30\nusers.filter(f=lambda id, user: user[\"age\"] \u003e= 30)\n```\n\n**Pretty neat, right?**\n\n\n\nSecond Example\n--------------------------------------------------------------------------------\n\nConsider the following dict filled with users. Notice how Bob has\nprovided sports interests only, and Julia has provided music interests only.\n```python\ndb = {\n    \"bob\": {\n        \"interests\": {\n            \"sports\": [\"soccer\", \"basketball\"]\n        }\n    },\n    \"julia\": {\n        \"interests\": {\n            \"music\": [\"pop\", \"alternative\"]\n        }\n    }\n}\n```\n\nLets print the music interests of each user using normal dicts:\n\n```python\nfor user_name in db:\n    user_music = None\n    if user_name in db:\n        if \"interests\" in db[user_name]:\n            if \"music\" in db[user_name][\"interests\"]:\n                user_music = db[user_name][\"interests\"][\"music\"]\n    print(user_music)\n\n# ---\u003e None\n# ---\u003e [\"pop\", \"alternative\"]\n```\n\n**Annoying, right?** This is how we do it with a PathDict:\n\n```python\ndb = PathDict(db)\nfor user_name in db:\n    print(db[user_name, \"interests\", \"music\"])\n\n# ---\u003e None\n# ---\u003e [\"pop\", \"alternative\"]\n\n```\n\n**Much better!** If any of the keys do not exist, it will not throw and error,\nbut return `None`.\n\nIf we tried this with a normal dict, we would have gotten a `KeyError`.\n\nThe same also works for setting values, if the path does not exist, it will be\ncreated.\n\nInstallation\n================================================================================\n\n`pip3 install path-dict`\n\n```python\nfrom path_dict import PathDict\n```\n\nUsage\n================================================================================\n\nPathDict subclasses [collections.UserDict](https://docs.python.org/3/library/collections.html#collections.UserDict),\nso it behaves almist like a normal python dict, but comes with some handy extras.\n\n## Initialize\n\n```python\n# Empty PathDict\npd = PathDict()\n\n\u003e pd\n---\u003e PathDict({})\n```\n\nA PathDict keeps a reference to the original initializing dict:\n\n```python\nuser = {\n    \"name\": \"Joe\",\n    \"age\": 22,\n    \"hobbies\": [\"Playing football\", \"Podcasts\"]\n    \"friends\": {\n        \"Sue\": {\"age\": 30},\n        \"Ben\": {\"age\": 35},\n    }\n}\njoe = PathDict(user)\n\u003e joe == user\n---\u003e True\n\u003e joe.dict is user\n---\u003e True\n```\n\nYou can also get a deep copy:\n\n```python\njoe = PathDict(user, copy=True)\n\u003e joe == user\n---\u003e True\n\u003e joe.dict is user\n---\u003e False\n```\n\n## Getting and setting values with paths\n\nYou can use paths of keys to access values:\n\n```python\njoe = PathDict(user, copy=True)\n\n# Get existing path\n\u003e joe[\"friends\", \"Sue\", \"age\"]\n---\u003e 30\n\n# Get non-existent, but valid path\n\u003e joe[\"friends\", \"Josef\", \"age\"]\n---\u003e None\n\n# Set non-existent, but valid path, creates keys\njoe[\"authentification\", \"password\"] = \"abc123\"\n\u003e joe[\"authentification\"]\n---\u003e PathDict({\"password\": \"abc123\"})\n```\n\nUsing invalid paths to get or set a value will result in an error. An invalid path is a path that tries to access a key of an int or list, for example. So, only use paths to access hierarchies of PathDicts.\n\n\n```python\njoe = PathDict(user, copy=True)\n\n# Get invalid path (joe[\"hobbies\"] is a list)\n\u003e joe[\"hobbies\", \"not_existent\"]\n---\u003e Error!\n```\n\n\n\n## Most dict methods are supported\n\nMany of the usual dict methods work with PathDict:\n\n```python\npathdict = ...\n\nfor key, value in pathdict.items():\n    ...\n\nfor key in pathdict:\n    ...\n\nfor key in pathdict.keys():\n    ...\n\nfor value in pathdict.values():\n    ...\n\n```\n\n## Apply a function at a path\n\nWhen setting a value, you can use a lambda function to modify the value at a given path.\nThe function should take one argument and return the modified value.\n\n\n```python\nstats_dict = {}\nstats_pd = PathDict({})\n\n# Using a standard dict:\nif \"views\" not in stats_dict:\n    stats_dict[\"views\"] = {}\nif \"total\" not in stats_dict[\"views\"]:\n     stats_dict[\"views\"][\"total\"] = 0\nstats_dict[\"views\"][\"total\"] += 1\n\n# Using a PathDict:\nstats_pd[\"views\", \"total\"] = lambda x: (x or 0) + 1\n```\n\n## Filtering\n\nPathDicts offer a filter function, which can filter a list or a PathDict at a given path in-place.\n\nTo filter a list, pass a function that takes one argument (eg. `lambda ele: return ele \u003e 10`) and returns True if the value should be kept, else False.\nTo filter a PathDict, pass a function that takes two arguments (eg. `lambda key, value: key != \"1\"`) and returns True if the key-value pair should be kept, else False.\n\nYou can filter the PathDict filter is called on, or you can also pass a path into the filter to apply the filter at a given path.\n\nA filtered function is also offered, which does the same, but returns a filtered copy instead of filtering in-place.\n\n\n```python\njoe = PathDict(user, copy=True)\n\n# Remove all friends that are older than 33.\njoe.filter(\"friends\", f=lambda k, v: v[\"age\"] \u003c= 33)\n\n\u003e joe[\"friends\"]\n---\u003e PathDict({\n    \"Sue\": {\"age\": 30}\n})\n```\n\n## Aggregating\n\nThe aggregate function can combine a PathDict to a single aggregated value.\nIt takes an init parameter, and a function with takes three arguments (eg. `lambda key, val, agg`)\n\n```python\njoe = PathDict(user, copy=True)\n\n# Sum of ages of all friends of joe\nfriend_ages = joe.aggregate(\"friends\", init=0, f=lambda k, v, a: a + v[\"age\"])\n\n\u003e friend_ages\n---\u003e 65\n```\n\n## Serialize to JSON\n\nTo serialize a PathDict to JSON, call `json.dumps(\u003cPathDict\u003e.dict)`.\nIf you try to serialize a PathDict object itself, the operation will fail.\n\n\n\n# Reference\n\n\n### pd(data: dict | list, raw=False) -\u003e PathDict\n\nCreates and returns a handle on the given data.\n\n Args:\n- `data` - Must be a list or dict.\n- `raw` - If `True`, do not interpret paths. So wildcards (`*`) are interpreted as a usual key, and tuples will be interpreted as keys  as well.\n\nReturns:\n- A handle that references the root of the given data dict or list.\n\n\n## PathDict\n\n### copy(self, from_root=False) -\u003e PathDict\n\nReturn a deep copy of the data at the current path or from the root.\n\nArgs:\n- `from_root` - If `True`, the copy will not be made at the root data, and not where the current path is. The path handle will be at the same location as it was in the original. If `False`, only the part of the data where the current path handle is at will be copied.\n\nReturns:\n- A handle on the newly created copy\n\nThe current path handle will stay the same.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkrd%2Fpathdict","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmkrd%2Fpathdict","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkrd%2Fpathdict/lists"}