{"id":22121205,"url":"https://github.com/ducdetronquito/scalpl","last_synced_at":"2025-04-12T22:26:15.249Z","repository":{"id":21026525,"uuid":"91627514","full_name":"ducdetronquito/scalpl","owner":"ducdetronquito","description":"A lightweight wrapper to operate on nested dictionaries seamlessly. 👌","archived":false,"fork":false,"pushed_at":"2023-04-10T04:20:57.000Z","size":222,"stargazers_count":199,"open_issues_count":3,"forks_count":16,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-28T00:14:18.419Z","etag":null,"topics":["addict","box","dict","dictionary","nested","python","python3","scalpl"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ducdetronquito.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2017-05-17T23:04:31.000Z","updated_at":"2025-03-06T01:04:38.000Z","dependencies_parsed_at":"2024-06-18T16:58:59.805Z","dependency_job_id":null,"html_url":"https://github.com/ducdetronquito/scalpl","commit_stats":{"total_commits":57,"total_committers":5,"mean_commits":11.4,"dds":"0.45614035087719296","last_synced_commit":"a11a6a6c3e1cabbc8afccbc4aa78b655e20bd413"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducdetronquito%2Fscalpl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducdetronquito%2Fscalpl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducdetronquito%2Fscalpl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducdetronquito%2Fscalpl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ducdetronquito","download_url":"https://codeload.github.com/ducdetronquito/scalpl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247103307,"owners_count":20884023,"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":["addict","box","dict","dictionary","nested","python","python3","scalpl"],"created_at":"2024-12-01T14:35:50.618Z","updated_at":"2025-04-04T01:10:38.432Z","avatar_url":"https://github.com/ducdetronquito.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":".. image:: https://raw.githubusercontent.com/ducdetronquito/scalpl/master/assets/scalpl.png\n    :target: https://github.com/ducdetronquito/scalpl\n\nScalpl\n======\n\n.. image:: https://img.shields.io/badge/license-public%20domain-ff69b4.svg\n    :target: https://github.com/ducdetronquito/scalpl#license\n\n.. image:: https://img.shields.io/badge/coverage-100%25-green.svg\n    :target: #\n\n.. image:: https://img.shields.io/badge/pypi-v0.4.2-blue.svg\n    :target: https://pypi.python.org/pypi/scalpl/\n\n.. image:: https://travis-ci.org/ducdetronquito/scalpl.svg?branch=master\n     :target: https://travis-ci.org/ducdetronquito/scalpl\n\n\nOutline\n~~~~~~~\n\n1. `Overview \u003chttps://github.com/ducdetronquito/scalpl#overview\u003e`_\n2. `Benefits \u003chttps://github.com/ducdetronquito/scalpl#benefits\u003e`_\n3. `Installation \u003chttps://github.com/ducdetronquito/scalpl#installation\u003e`_\n4. `Usage \u003chttps://github.com/ducdetronquito/scalpl#usage\u003e`_\n5. `Benchmark \u003chttps://github.com/ducdetronquito/scalpl#benchmark\u003e`_\n6. `Frequently Asked Questions \u003chttps://github.com/ducdetronquito/scalpl#frequently-asked-questions\u003e`_\n7. `How to Contribute \u003chttps://github.com/ducdetronquito/scalpl#how-to-contribute\u003e`_\n8. `License \u003chttps://github.com/ducdetronquito/scalpl#license\u003e`_\n\n\nOverview\n~~~~~~~~\n\n\n**Scalpl** provides a **lightweight wrapper** that helps you to operate\non **nested dictionaries** seamlessly **through the built-in** ``dict``\n**API**, by using dot-separated string keys.\n\nIt's not a drop-in replacement for your dictionnaries, just syntactic\nsugar to avoid ``this['annoying']['kind']['of']['things']`` and\n``prefer['a.different.approach']``.\n\nNo conversion cost, a thin computation overhead: that's **Scalpl** in a\nnutshell.\n\n\nBenefits\n~~~~~~~~\n\nThere are a lot of good libraries to operate on nested dictionaries,\nsuch as `Addict \u003chttps://github.com/mewwts/addict\u003e`_ or \n`Box \u003chttps://github.com/cdgriffith/Box\u003e`_ , but if you give **Scalpl**\na try, you will find it:\n\n* 🚀 Powerful as the standard dict API\n* ⚡ Lightweight\n* 👌 Well tested\n\n\nInstallation\n~~~~~~~~~~~~\n\n**Scalpl** is a Python3 library that you can install via ``pip``\n\n.. code:: sh\n\n    pip3 install scalpl\n\n\nUsage\n~~~~~\n\n**Scalpl** provides a simple class named **Cut** that wraps around your dictionary\nand handles operations on nested ``dict`` and that can cut accross ``list`` item.\n\nThis wrapper strictly follows the standard ``dict``\n`API \u003chttps://docs.python.org/3/library/stdtypes.html#dict\u003e`_, which\nmeans you can operate seamlessly on ``dict``,\n``collections.defaultdict`` or ``collections.OrderedDict`` by using their methods\nwith dot-separated keys.\n \nLet's see what it looks like with an example ! 👇\n\n.. code:: python\n\n    from scalpl import Cut\n\n    data = {\n        'pokemon': [\n            {\n                'name': 'Bulbasaur',\n                'type': ['Grass', 'Poison'],\n                'category': 'Seed',\n                'ability': 'Overgrow'\n            },\n            {   \n                'name': 'Charmander',\n                'type': 'Fire',\n                'category': 'Lizard',\n                'ability': 'Blaze',\n            },\n            {\n                'name': 'Squirtle',\n                'type': 'Water',\n                'category': 'Tiny Turtle',\n                'ability': 'Torrent',\n            }\n        ],\n        'trainers': [\n            {\n                'name': 'Ash',\n                'hometown': 'Pallet Town'\n            }\n        ]\n    }\n    # Just wrap your data, and you're ready to go deeper !\n    proxy = Cut(data)\n\nYou can use the built-in ``dict`` API to access its values.\n\n.. code:: python\n\n    proxy['pokemon[0].name']\n    # 'Bulbasaur'\n    proxy.get('pokemon[1].sex', 'Unknown')\n    # 'Unknown'\n    'trainers[0].hometown' in proxy\n    # True\n\nBy default, **Scalpl** uses dot as a key separator, but you are free to\nuse a different character that better suits your needs.\n\n.. code:: python\n\n    # You just have to provide one when you wrap your data.\n    proxy = Cut(data, sep='-\u003e')\n    # Yarrr!\n    proxy['pokemon[0]-\u003ename']\n\nYou can also easily create or update any key/value pair.\n\n.. code:: python\n\n    proxy['pokemon[1].weaknesses'] = ['Ground', 'Rock', 'Water']\n    proxy['pokemon[1].weaknesses']\n    # ['Ground', 'Rock', 'Water']\n    proxy.update({\n        'trainers[0].region': 'Kanto',\n    })\n\n\nFollowing its purpose in the standard API, the *setdefault* method allows\nyou to create any missing dictionary when you try to access a nested key.\n\n.. code:: python\n\n    proxy.setdefault('pokemon[2].moves.Scratch.power', 40)\n    # 40\n\n\nAnd it is still possible to iterate over your data.\n\n.. code:: python\n\n    proxy.items()\n    # [('pokemon', [...]), ('trainers', [...])]\n    proxy.keys()\n    # ['pokemon', 'trainers']\n    proxy.values()\n    # [[...], [...]]\n\nBy the way, if you have to operate on a list of dictionaries, the\n``Cut.all`` method is what you are looking for.\n\n.. code:: python\n\n    # Let's teach these pokemon some sick moves !\n    for pokemon in proxy.all('pokemon'):\n        pokemon.setdefault('moves.Scratch.power', 40)\n\nAlso, you can remove a specific or an arbitrary key/value pair.\n\n.. code:: python\n\n    proxy.pop('pokemon[0].category')\n    # 'Seed'\n    proxy.popitem()\n    # ('trainers', [...])\n    del proxy['pokemon[1].type']\n\nBecause **Scalpl** is only a wrapper around your data, it means you can\nget it back at will without any conversion cost. If you use an external\nAPI that operates on dictionary, it will just work.\n\n.. code:: python\n\n    import json\n    json.dumps(proxy.data)\n    # \"{'pokemon': [...]}\"\n\nFinally, you can retrieve a shallow copy of the inner dictionary or\nremove all keys.\n\n.. code:: python\n\n    shallow_copy = proxy.copy()\n\n    proxy.clear()\n\n\nBenchmark\n~~~~~~~~~\n\nThis humble benchmark is an attempt to give you an overview of the performance\nof `Scalpl \u003chttps://github.com/ducdetronquito/scalpl\u003e`_ compared to `Addict \u003chttps://github.com/mewwts/addict\u003e`_,\n`Box \u003chttps://github.com/cdgriffith/Box\u003e`_ and the built-in ``dict``.\n\nIt will summarize the *number of operations per second* that each library is \nable to perform on a portion of the JSON dump of the `Python subreddit main page \u003chttps://www.reddit.com/r/Python.json\u003e`_.\n\nYou can run this benchmark on your machine with the following command:\n\n    python3 ./benchmarks/performance_comparison.py\n\nHere are the results obtained on an Intel Core i5-7500U CPU (2.50GHz) with **Python 3.6.4**.\n\n\n**Addict** 2.2.1::\n\n    instantiate:-------- 271,132  ops per second.\n    get:---------------- 276,090  ops per second.\n    get through list:--- 293,773  ops per second.\n    set:---------------- 300,324  ops per second.\n    set through list:--- 282,149  ops per second.\n\n\n**Box** 3.4.2::\n\n    instantiate:--------- 4,093,439  ops per second.\n    get:-----------------   957,069  ops per second.\n    get through list:----   164,013  ops per second.\n    set:-----------------   900,466  ops per second.\n    set through list:----   165,522  ops per second.\n\n\n**Scalpl** latest::\n\n    instantiate:-------- 183,879,865  ops per second.\n    get:----------------  14,941,355  ops per second.\n    get through list:---  14,175,349  ops per second.\n    set:----------------  11,320,968  ops per second.\n    set through list:---  11,956,001  ops per second.\n\n\n**dict**::\n\n    instantiate:---------  37,816,714  ops per second.\n    get:-----------------  84,317,032  ops per second.\n    get through list:----  62,480,474  ops per second.\n    set:----------------- 146,484,375  ops per second.\n    set through list :--- 122,473,974  ops per second.\n\n\nAs a conclusion and despite being an order of magniture slower than the built-in\n``dict``, **Scalpl** is faster than Box and Addict by an order of magnitude for any operations.\nBesides, the gap increase in favor of **Scalpl** when wrapping large dictionaries.\n\nKeeping in mind that this benchmark may vary depending on your use-case, it is very unlikely that\n**Scalpl** will become a bottleneck of your application.\n\n\nFrequently Asked Questions:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n* **What if my keys contain dots ?**\n    If your keys contain a lot of dots, you should use an other\n    key separator when wrapping your data::\n\n        proxy = Cut(data, sep='-\u003e')\n        proxy['computer-\u003enetwork-\u003e127.0.0.1']\n\n    Otherwise, split your key in two part::\n\n        proxy = Cut(data)\n        proxy['computer.network']['127.0.0.1']\n\n* **What if my keys contain spaces ?**::\n    \n    proxy = Cut(data)\n    proxy['it works perfectly'] = 'fine'\n\n\nHow to Contribute\n~~~~~~~~~~~~~~~~~\n\nContributions are welcomed and anyone can feel free to submit a patch, report a bug or ask for a feature. Please open an issue first in order to encourage and keep tracks of potential discussions ✍️\n\n\nLicense\n~~~~~~~\n\n**Scalpl** is released into the **Public Domain**. 🎉\n\nPs: If we meet some day, and you think this small stuff worths it, you\ncan give me a beer, a coffee or a high-five in return: I would be really\nhappy to share a moment with you ! 🍻\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fducdetronquito%2Fscalpl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fducdetronquito%2Fscalpl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fducdetronquito%2Fscalpl/lists"}