Package: scalpl
ScalplOutlineOverviewBenefitsInstallationUsageBenchmarkFrequently Asked QuestionsHow to ContributeLicenseOverviewScalplprovides alightweight wrapperthat helps you to operate
onnested dictionariesseamlesslythrough the built-indictAPI, by using dot-separated string keys.It’s not a drop-in replacement for your dictionnaries, just syntactic
sugar to avoidthis['annoying']['kind']['of']['things']andprefer['a.different.approach'].No conversion cost, a thin computation overhead: that’sScalplin a
nutshell.BenefitsThere are a lot of good libraries to operate on nested dictionaries,
such asAddictorBox, but if you giveScalpla try, you will find it:🚀 Powerful as the standard dict API⚡ Lightweight👌 Well testedInstallationScalplis a Python3 library that you can install viapippip3installscalplUsageScalplprovides a simple class namedCutthat wraps around your dictionary
and handles operations on nesteddictand that can cut accrosslistitem.This wrapper strictly follows the standarddictAPI, which
means you can operate seamlessly ondict,collections.defaultdictorcollections.OrderedDictby using their methods
with dot-separated keys.Let’s see what it looks like with an example ! 👇fromscalplimportCutdata={'pokemon':[{'name':'Bulbasaur','type':['Grass','Poison'],'category':'Seed','ability':'Overgrow'},{'name':'Charmander','type':'Fire','category':'Lizard','ability':'Blaze',},{'name':'Squirtle','type':'Water','category':'Tiny Turtle','ability':'Torrent',}],'trainers':[{'name':'Ash','hometown':'Pallet Town'}]}# Just wrap your data, and you're ready to go deeper !proxy=Cut(data)You can use the built-indictAPI to access its values.proxy['pokemon[0].name']# 'Bulbasaur'proxy.get('pokemon[1].sex','Unknown')# 'Unknown''trainers[0].hometown'inproxy# TrueBy default,Scalpluses dot as a key separator, but you are free to
use a different character that better suits your needs.# You just have to provide one when you wrap your data.proxy=Cut(data,sep='->')# Yarrr!proxy['pokemon[0]->name']You can also easily create or update any key/value pair.proxy['pokemon[1].weaknesses']=['Ground','Rock','Water']proxy['pokemon[1].weaknesses']# ['Ground', 'Rock', 'Water']proxy.update({'trainers[0].region':'Kanto',})Following its purpose in the standard API, thesetdefaultmethod allows
you to create any missing dictionary when you try to access a nested key.proxy.setdefault('pokemon[2].moves.Scratch.power',40)# 40And it is still possible to iterate over your data.proxy.items()# [('pokemon', [...]), ('trainers', [...])]proxy.keys()# ['pokemon', 'trainers']proxy.values()# [[...], [...]]By the way, if you have to operate on a list of dictionaries, theCut.allmethod is what you are looking for.# Let's teach these pokemon some sick moves !forpokemoninproxy.all('pokemon'):pokemon.setdefault('moves.Scratch.power',40)Also, you can remove a specific or an arbitrary key/value pair.proxy.pop('pokemon[0].category')# 'Seed'proxy.popitem()# ('trainers', [...])delproxy['pokemon[1].type']BecauseScalplis only a wrapper around your data, it means you can
get it back at will without any conversion cost. If you use an external
API that operates on dictionary, it will just work.importjsonjson.dumps(proxy.data)# "{'pokemon': [...]}"Finally, you can retrieve a shallow copy of the inner dictionary or
remove all keys.shallow_copy=proxy.copy()proxy.clear()BenchmarkThis humble benchmark is an attempt to give you an overview of the performance
ofScalplcompared toAddict,Boxand the built-indict.It will summarize thenumber of operations per secondthat each library is
able to perform on a portion of the JSON dump of thePython subreddit main page.You can run this benchmark on your machine with the following command:python3 ./benchmarks/performance_comparison.pyHere are the results obtained on an Intel Core i5-7500U CPU (2.50GHz) withPython 3.6.4.Addict2.2.1:instantiate:-------- 271,132  ops per second.
get:---------------- 276,090  ops per second.
get through list:--- 293,773  ops per second.
set:---------------- 300,324  ops per second.
set through list:--- 282,149  ops per second.Box3.4.2:instantiate:--------- 4,093,439  ops per second.
get:-----------------   957,069  ops per second.
get through list:----   164,013  ops per second.
set:-----------------   900,466  ops per second.
set through list:----   165,522  ops per second.Scalpllatest:instantiate:-------- 183,879,865  ops per second.
get:----------------  14,941,355  ops per second.
get through list:---  14,175,349  ops per second.
set:----------------  11,320,968  ops per second.
set through list:---  11,956,001  ops per second.dict:instantiate:---------  37,816,714  ops per second.
get:-----------------  84,317,032  ops per second.
get through list:----  62,480,474  ops per second.
set:----------------- 146,484,375  ops per second.
set through list :--- 122,473,974  ops per second.As a conclusion and despite being an order of magniture slower than the built-indict,Scalplis faster than Box and Addict by an order of magnitude for any operations.
Besides, the gap increase in favor ofScalplwhen wrapping large dictionaries.Keeping in mind that this benchmark may vary depending on your use-case, it is very unlikely thatScalplwill become a bottleneck of your application.Frequently Asked Questions:What if my keys contain dots ?If your keys contain a lot of dots, you should use an other
key separator when wrapping your data:proxy = Cut(data, sep='->')
proxy['computer->network->127.0.0.1']Otherwise, split your key in two part:proxy = Cut(data)
proxy['computer.network']['127.0.0.1']What if my keys contain spaces ?:proxy = Cut(data)
proxy['it works perfectly'] = 'fine'How to ContributeContributions 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 ✍️LicenseScalplis released into thePublic Domain. 🎉Ps: If we meet some day, and you think this small stuff worths it, you
can give me a beer, a coffee or a high-five in return: I would be really
happy to share a moment with you ! 🍻