Change nesting order of triple-nested Python dictionary - python

I have dictionary with three-level nesting, eg:
d = {
'sp1':{
'a1':{'c1':2,'c2':3},
'a2':{'c3':1,'c4':4}
},
'sp2':{
'a1':{'c1':3,'c2':3},
'a2':{'c3':2,'c4':0}
}
}
All 2nd-level dictionaries contain the same elements, so I want to change it to
d2 = {'a1':{'c1':{'sp1':2,'sp2':3}, 'c2':{'sp1':3,'sp2':3}}}
i.e. essentially switch nesting order. But when I write code like
d2 = {}
d2['a1']['c1']['sp1'] = 2
It just throws KeyError with whatever values happens to be 'a1'. How do I perform such operation?

If you are doing it manually like the snippet you tried, this is how you should be doing it:
>>> d = {
... 'sp1':{
... 'a1':{'c1':2,'c2':3},
... 'a2':{'c3':1,'c4':4}
... },
... 'sp2':{
... 'a1':{'c1':3,'c2':3},
... 'a2':{'c3':2,'c4':0}
... }
... }
>>>
>>> e = {}
>>> e['a1'] = {}
>>> e['a1']['c1'] = {}
>>> e['a1']['c1']['sp1'] = d['sp1']['a1']['c1']
>>> e['a1']['c2'] = {}
>>> e['a1']['c2']['sp1'] = d['sp1']['a1']['c2']
>>> e['a2'] = {}
>>> e['a2']['c1'] = {}
>>> e['a2']['c2'] = {}
>>> e['a1']['c1']['sp2'] = d['sp2']['a1']['c1']
>>> e['a1']['c2']['sp2'] = d['sp2']['a1']['c2']
>>> e
{'a1': {'c2': {'sp1': 3, 'sp2': 3}, 'c1': {'sp1': 2, 'sp2': 3}}}
>>>
But it is unclear as to why you are doing it. As OmnipotentEntity suggested in the comments, may be you need to use a different data structure to store the data.

To do this, you can use defaultdict, which allows you to define a default initialization action on a dict.
In your case, you want a reverse-order recursive defaultdict, with a classmethod
reverse_recursive_make() which unrolls and reverses the key order:
when passed in a key-value pair or None, returns a (toplevel) dict
when passed in a dict, recurses into each of the {k:v} pairs
I'm not going to write the code for that because what you want can be much more easily achieved with SQL, like I commented.
FOOTNOTE: your version with lambdas (comment below) is perfect.
(If you insist on using dicts, and not some other data structure)

something like this should work
d_final = {}
for k in d.keys():
d2 = d[k]
for k2 in d2.keys():
d3 = d2[k2]
for k3 in d3.keys():
d4 = d_final.get(k2,{})
d4[k] = d3[k3]
d_final[k2] = d4
I may have my indexing off a little, but that should be about right.

Related

Bast way to update a value in a dict

I have a dict that contains parameters. I want to consider that any unspecified parameter should be read as a zero. I see several ways of doing it and wonder which one is recommended:
parameters = {
"apples": 2
}
def gain_fruits0 (quantity, fruit):
if not fruit in parameters :
parameters[fruit] = 0
parameters[fruit] += quantity
def gain_fruits1 (quantity, fruits):
parameters[fruit] = quantity + parameters.get(fruit,0)
parameters is actually way bigger than that, if that is important to know for optimization purposes.
So, what would be the best way? gain_fruits0, gain_fruits1, or something else?
This is a typical use of defaultdict, which works exactly like a regular dictionary except that it has the functionality you're after built in:
>>> from collections import defaultdict
>>> d = defaultdict(int) # specify default value `int()`, which is 0
>>> d['apples'] += 1
>>> d
defaultdict(int, {'apples': 1})
>>> d['apples'] # can index normally
1
>>> d['oranges'] # missing keys map to the default value
0
>>> dict(d) # can also cast to regular dict
{'apples': 1, 'oranges': 0}

Is it possible for a key to have multiple names in a dictionary?

I'm not sure if this is even possible but it's worth a shot asking.
I want to be able to access the value from indexing one of the values.
The first thing that came to mind was this but of course, it didn't work.
dict = {['name1', 'name2'] : 'value1'}
print(dict.get('name1))
You can use a tuple (as it's immutable) as a dict key if you need to access it by a pair (or more) of strings (or other immutable values):
>>> d = {}
>>> d[("foo", "bar")] = 6
>>> d[("foo", "baz")] = 8
>>> d
{('foo', 'bar'): 6, ('foo', 'baz'): 8}
>>> d[("foo", "baz")]
8
>>>
This isn't "a key having multiple names", though, it's just a key that happens to be built of multiple strings.
Edit
As discussed in the comments, the end goal is to have multiple keys for each (static) value. That can be succinctly accomplished with an inverted dict first, which is then "flipped" using dict.fromkeys():
def foobar():
pass
def spameggs():
pass
func_to_names = {
foobar: ("foo", "bar", "fb", "foobar"),
spameggs: ("spam", "eggs", "se", "breakfast"),
}
name_to_func = {}
for func, names in func_to_names.items():
name_to_func.update(dict.fromkeys(names, func))
If we tried it you way using:
# Creating a dictionary
myDict = {[1, 2]: 'Names'}
print(myDict)
We get an output of:
TypeError: unhashable type: 'list'
To get around this, we can use this method:
# Creating an empty dictionary
myDict = {}
# Adding list as value
myDict["key1"] = [1, 2]
myDict["key2"] = ["Jim", "Jeff", "Jack"]
print(myDict)

Save a dictionary key as a variable

I'm working on a small framework and I've found a place where it would be beneficial to save a dictionary key as variable.
The problem I have is that the dictionary may have any number of layers, so it's not just a case of storing the final key. For example in the below I am accessing ['dig']['result'], but that could equally be ['output'] or ['some']['thing']['strange']
if result:
if self.cli_args.json:
pprint(result)
else:
print result['dig']['result']
I could save the key as a string and use eval() in something such as:
key="['test']"
test_dict = { "test" : "This works" }
eval("test_dict" + key)
>>> 'This works'
But eval is really dirty right? :-)
Is there a nice / pythonic way to do this?
To handle an arbitrary depth of key nesting, you can iterate over a sequence (e.g. tuple) of the keys:
>>> d = {'a': {'b': {'c': 'd'}}}
>>> d['a']['b']['c']
'd'
>>> keys = ('a', 'b', 'c') # or just 'abc' for this trivial example
>>> content = d
>>> for k in keys:
content = content[k]
>>> content
'd'
>>> def access(o,path):
... for k in path.split('/'):
... o = o[k]
... return o
...
>>> access({'a': {'b': {'c': 'd'}}},'a/b/c')
'd'

Storing list data into a dictionary

I'm trying to convert some data in a list to a dictionary. However, I want this dictionary to have multiple keys with multiple values, for example:
The list:
titles = [ "title1", "title2" ]
ids = [ 1, 2 ]
The dictionary:
dictionary = { "title" : "title1", "title2", "id" : 1, 2 }
Sort of like a JSON idea...
There are actually quite a few entries in the lists, so I can't do them each manually. I could use a loop, I suppose?
You probably want a defaultdict:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> d['title'].append('title1')
>>> d['title'].append('title2')
>>> print d
defaultdict(<type 'list'>, {'title': ['title1', 'title2']})
In your case, however, you can simply do:
dictionary = {'title':titles, 'id':ids}
You can link items together using tuples also if you'd prefer (using parentheses). This way you can access multiple items with one key. I don't quite know if this is what you're looking for, but I hope it helps.
dictionary = { "title" : ("title1", "title2"), "id" : (1, 2) }
#dictionary[title][0] returns "title1"
#dictionary[title][1] returns "title2"
More info on tuples:
http://docs.python.org/release/1.5.1p1/tut/tuples.html
you can also try this:
>>> titles = ["t1","t2"]
>>> ids = [1,2]
>>> dct = {"titles": titles, "ids":ids}
>>> dct
{'titles': ['t1', 't2'], 'ids': [1, 2]}
Another solution is using dict setdefault function
>>> d = {}
>>> d.setdefault('title', []).append('title1')
>>> d.setdefault('title', []).append('title2')
>>> d
{'title': ['title1', 'title2']}

Destructuring-bind dictionary contents

I am trying to 'destructure' a dictionary and associate values with variables names after its keys. Something like
params = {'a':1,'b':2}
a,b = params.values()
But since dictionaries are not ordered, there is no guarantee that params.values() will return values in the order of (a, b). Is there a nice way to do this?
from operator import itemgetter
params = {'a': 1, 'b': 2}
a, b = itemgetter('a', 'b')(params)
Instead of elaborate lambda functions or dictionary comprehension, may as well use a built in library.
One way to do this with less repetition than Jochen's suggestion is with a helper function. This gives the flexibility to list your variable names in any order and only destructure a subset of what is in the dict:
pluck = lambda dict, *args: (dict[arg] for arg in args)
things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')
Also, instead of joaquin's OrderedDict you could sort the keys and get the values. The only catches are you need to specify your variable names in alphabetical order and destructure everything in the dict:
sorted_vals = lambda dict: (t[1] for t in sorted(dict.items()))
things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)
How come nobody posted the simplest approach?
params = {'a':1,'b':2}
a, b = params['a'], params['b']
Python is only able to "destructure" sequences, not dictionaries. So, to write what you want, you will have to map the needed entries to a proper sequence. As of myself, the closest match I could find is the (not very sexy):
a,b = [d[k] for k in ('a','b')]
This works with generators too:
a,b = (d[k] for k in ('a','b'))
Here is a full example:
>>> d = dict(a=1,b=2,c=3)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> a, b = [d[k] for k in ('a','b')]
>>> a
1
>>> b
2
>>> a, b = (d[k] for k in ('a','b'))
>>> a
1
>>> b
2
Here's another way to do it similarly to how a destructuring assignment works in JS:
params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
What we did was to unpack the params dictionary into key values (using **) (like in Jochen's answer), then we've taken those values in the lambda signature and assigned them according to the key name - and here's a bonus - we also get a dictionary of whatever is not in the lambda's signature so if you had:
params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
After the lambda has been applied, the rest variable will now contain:
{'c': 3}
Useful for omitting unneeded keys from a dictionary.
Hope this helps.
Maybe you really want to do something like this?
def some_func(a, b):
print a,b
params = {'a':1,'b':2}
some_func(**params) # equiv to some_func(a=1, b=2)
If you are afraid of the issues involved in the use of the locals dictionary and you prefer to follow your original strategy, Ordered Dictionaries from python 2.7 and 3.1 collections.OrderedDicts allows you to recover you dictionary items in the order in which they were first inserted
(Ab)using the import system
The from ... import statement lets us desctructure and bind attribute names of an object. Of course, it only works for objects in the sys.modules dictionary, so one could use a hack like this:
import sys, types
mydict = {'a':1,'b':2}
sys.modules["mydict"] = types.SimpleNamespace(**mydict)
from mydict import a, b
A somewhat more serious hack would be to write a context manager to load and unload the module:
with obj_as_module(mydict, "mydict_module"):
from mydict_module import a, b
By pointing the __getattr__ method of the module directly to the __getitem__ method of the dict, the context manager can also avoid using SimpleNamespace(**mydict).
See this answer for an implementation and some extensions of the idea.
One can also temporarily replace the entire sys.modules dict with the dict of interest, and do import a, b without from.
Warning 1: as stated in the docs, this is not guaranteed to work on all Python implementations:
CPython implementation detail: This function relies on Python stack frame support
in the interpreter, which isn’t guaranteed to exist in all implementations
of Python. If running in an implementation without Python stack frame support
this function returns None.
Warning 2: this function does make the code shorter, but it probably contradicts the Python philosophy of being as explicit as you can. Moreover, it doesn't address the issues pointed out by John Christopher Jones in the comments, although you could make a similar function that works with attributes instead of keys. This is just a demonstration that you can do that if you really want to!
def destructure(dict_):
if not isinstance(dict_, dict):
raise TypeError(f"{dict_} is not a dict")
# the parent frame will contain the information about
# the current line
parent_frame = inspect.currentframe().f_back
# so we extract that line (by default the code context
# only contains the current line)
(line,) = inspect.getframeinfo(parent_frame).code_context
# "hello, key = destructure(my_dict)"
# -> ("hello, key ", "=", " destructure(my_dict)")
lvalues, _equals, _rvalue = line.strip().partition("=")
# -> ["hello", "key"]
keys = [s.strip() for s in lvalues.split(",") if s.strip()]
if missing := [key for key in keys if key not in dict_]:
raise KeyError(*missing)
for key in keys:
yield dict_[key]
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}
In [6]: hello, key = destructure(my_dict)
In [7]: hello
Out[7]: 'world'
In [8]: key
Out[8]: 'value'
This solution allows you to pick some of the keys, not all, like in JavaScript. It's also safe for user-provided dictionaries
With Python 3.10, you can do:
d = {"a": 1, "b": 2}
match d:
case {"a": a, "b": b}:
print(f"A is {a} and b is {b}")
but it adds two extra levels of indentation, and you still have to repeat the key names.
Look for other answers as this won't cater to the unexpected order in the dictionary. will update this with a correct version sometime soon.
try this
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
keys = data.keys()
a,b,c = [data[k] for k in keys]
result:
a == 'Apple'
b == 'Banana'
c == 'Carrot'
Well, if you want these in a class you can always do this:
class AttributeDict(dict):
def __init__(self, *args, **kwargs):
super(AttributeDict, self).__init__(*args, **kwargs)
self.__dict__.update(self)
d = AttributeDict(a=1, b=2)
Based on #ShawnFumo answer I came up with this:
def destruct(dict): return (t[1] for t in sorted(dict.items()))
d = {'b': 'Banana', 'c': 'Carrot', 'a': 'Apple' }
a, b, c = destruct(d)
(Notice the order of items in dict)
An old topic, but I found this to be a useful method:
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
for key in data.keys():
locals()[key] = data[key]
This method loops over every key in your dictionary and sets a variable to that name and then assigns the value from the associated key to this new variable.
Testing:
print(a)
print(b)
print(c)
Output
Apple
Banana
Carrot
An easy and simple way to destruct dict in python:
params = {"a": 1, "b": 2}
a, b = [params[key] for key in ("a", "b")]
print(a, b)
# Output:
# 1 2
I don't know whether it's good style, but
locals().update(params)
will do the trick. You then have a, b and whatever was in your params dict available as corresponding local variables.
Since dictionaries are guaranteed to keep their insertion order in Python >= 3.7, that means that it's complete safe and idiomatic to just do this nowadays:
params = {'a': 1, 'b': 2}
a, b = params.values()
print(a)
print(b)
Output:
1
2

Categories

Resources