using a single variable to index into nested dictionaries - python

Imagine you have a dictionary in python: myDic = {'a':1, 'b':{'c':2, 'd':3}}. You can certainly set a variable to a key value and use it later, such as:
myKey = 'b'
myDic[myKey]
>>> {'c':2, 'd':3}
However, is there a way to somehow set a variable to a value that, when used as a key, will dig into sub dictionaries as well? Is there a way to accomplish the following pseudo-code in python?
myKey = "['b']['c']"
myDic[myKey]
>>> 2
So first it uses 'b' as a key, and whatever is reurned it then uses 'c' as a key on that. Obviously, it would return an error if the value returned from the first lookup is not a dictionary.

No, there is nothing you can put into a variable so that myDict[myKey] will dig into the nested dictionaries.
Here is a function that may work for you as an alternative:
def recursive_get(d, keys):
if len(keys) == 1:
return d[keys[0]]
return recursive_get(d[keys[0]], keys[1:])
Example:
>>> myDic = {'a':1, 'b':{'c':2, 'd':3}}
>>> recursive_get(myDic, ['b', 'c'])
2

No, not with a regular dict. With myDict[key] you can only access values that are actually values of myDict. But if myDict contains other dicts, the values of those nested dicts are not values of myDict.
Depending on what you're doing with the data structure, it may be possible to get what you want by using tuple keys instead of nested dicts. Instead of having myDic = {'b':{'c':2, 'd':3}}, you could have myDic = {('b', 'c'):2, ('b', 'd'): 3}. Then you can access the values with something like myDic['b', 'c']. And you can indeed do:
val = 'b', 'c'
myDic[val]

AFAIK, you cannot. If you think about the way python works, it evaluates inside out, left to right. [] is a shorthand for __getitem__ in this case. Thus you would need to parse the arguments you are passing into __getitem__ (whatever you pass in) and handle that intelligently. If you wanted to have such behavior, you would need to subclass/write your own dict class.

myDict = {'a':1, 'b':{'c':2, 'd':3}}
k = 'b'
myDict.get(k) should give
{'c':2, 'd':3}
and either
d.get(k)['c']
OR
k1 = 'c'
d.get(k).key(k1) should give 2

Pretty old question. There is no builtin function for that.
Compact solution using functools.reduce and operator.getitem:
from functools import reduce
from operator import getitem
d = {'a': {'b': ['banana', 'lemon']}}
p = ['a', 'b', 1]
v = reduce(getitem, p, d)
# 'lemon'

Related

Function to create dictionary with default values that can be either immutable or mutable

I have a function to create a dictionary with specific keys, which accepts a parameter to specify what each key's "default" value should be.
def new_dict(entrys_default=0):
my_dict = {}
for k in ['a', 'b', 'c']:
my_dict[k] = entrys_default
return my_dict
The issue is that when I call it with new_dict(entrys_default=[]) so that each entry in the dictionary is created with a new empty list as its value, when I then update one entry with returned_dict['a'].append(123) then all entries are updated:
{'a': [123], 'b': [123], 'c': [123]}
This doesn't happen when using an integer, and I understand that it is because the entrys_default is immutable when it is an integer, but is a reference to the same list when it is a list or dictionary.
I want to be able to have this function work the same as it does for integer parameters with lists and dictionaries as entrys_default - i.e. each entry has its own list/dictionary - but want to keep the function flexible to also work for integers.
Can anyone please suggest the best way to go about this?
Do what collections.defaultdict does; instead of taking an "example" default value, take a function that returns the desired default value. Then call that function and use its return value to initialize each element of the dict being constructed.
def new_dict(make_default=int): # int() == 0
my_dict = {}
for k in ['a', 'b', 'c']:
my_dict[k] = make_default()
return my_dict
d = new_dict(list) # list() == [], but a distinct list each time it is called
d['a'].append(123)
assert d['a'] != d['b']

Merging values from 2 dictionaries (Python)

(I'm new to Python!)
Trying to figure out this homework question:
The function will takes a​s input​ two dictionaries, each mapping strings to integers. The function will r​eturn​ a dictionary that maps strings from the two input dictionaries to the sum of the integers in the two input dictionaries.
my idea was this:
def ​add(​dicA,dicB):
dicA = {}
dicB = {}
newdictionary = dicA.update(dicB)
however, that brings back None.
In the professor's example:
print(add({'alice':10, 'Bob':3, 'Carlie':1}, {'alice':5, 'Bob':100, 'Carlie':1}))
the output is:
{'alice':15, 'Bob':103, 'Carlie':2}
My issue really is that I don't understand how to add up the values from each dictionaries. I know that the '+' is not supported with dictionaries. I'm not looking for anyone to do my homework for me, but any suggestions would be very much appreciated!
From the documentation:
update([other])
Update the dictionary with the key/value pairs from other, overwriting existing keys. Return None.
You don't want to replace key/value pairs, you want to add the values for similar keys. Go through each dictionary and add each value to the relevant key:
def ​add(​dicA,dicB):
result = {}
for d in dicA, dicB:
for key in d:
result[key] = result.get(key, 0) + d[key]
return result
result.get(key, 0) will retrieve the value of an existing key or produce 0 if key is not yet present.
First of all, a.update(b) updates a in place, and returns None.
Secondly, a.update(b) wouldn't help you to sum the keys; it would just produce a dictionary with the resulting dictionary having all the key, value pairs from b:
>>> a = {'alice':10, 'Bob':3, 'Carlie':1}
>>> b = {'alice':5, 'Bob':100, 'Carlie':1}
>>> a.update(b)
>>> a
{'alice': 5, 'Carlie': 1, 'Bob': 100}
It'd be easiest to use collections.Counter to achieve the desired result. As a plus, it does support addition with +:
from collections import Counter
def add(dicA, dicB):
return dict(Counter(dicA) + Counter(dicB))
This produces the intended result:
>>> print(add({'alice':10, 'Bob':3, 'Carlie':1}, {'alice':5, 'Bob':100, 'Carlie':1}))
{'alice': 15, 'Carlie': 2, 'Bob': 103}
The following is not meant to be the most elegant solution, but to get a feeling on how to deal with dicts.
dictA = {'Alice':10, 'Bob':3, 'Carlie':1}
dictB = {'Alice':5, 'Bob':100, 'Carlie':1}
# how to iterate through a dictionary
for k,v in dictA.iteritems():
print k,v
# make a new dict to keep tally
newdict={}
for d in [dictA,dictB]: # go through a list that has your dictionaries
print d
for k,v in d.iteritems(): # go through each dictionary item
if not k in newdict.keys():
newdict[k]=v
else:
newdict[k]+=v
print newdict
Output:
Bob 3
Alice 10
Carlie 1
{'Bob': 3, 'Alice': 10, 'Carlie': 1}
{'Bob': 100, 'Alice': 5, 'Carlie': 1}
{'Bob': 103, 'Alice': 15, 'Carlie': 2}
def ​add(​dicA,dicB):
You define a function that takes two arguments, dicA and dicB.
dicA = {}
dicB = {}
Then you assign an empty dictionary to both those variables, overwriting the dictionaries you passed to the function.
newdictionary = dicA.update(dicB)
Then you update dicA with the values from dicB, and assign the result to newdictionary. dict.update always returns None though.
And finally, you don’t return anything from the function, so it does not give you any results.
In order to combine those dictionaries, you actually need to use the values that were passed to it. Since dict.update mutates the dictionary it is called on, this would change one of those passed dictionaries, which we generally do not want to do. So instead, we use an empty dictionary, and then copy the values from both dictionaries into it:
def add (dicA, dicB):
newDictionary = {}
newDictionary.update(dicA)
newDictionary.update(dicB)
return newDictionary
If you want the values to sum up automatically, then use a Counter instead of a normal dictionary:
from collections import Counter
def add (dicA, dicB):
newDictionary = Counter()
newDictionary.update(dicA)
newDictionary.update(dicB)
return newDictionary
I suspect your professor wants to achieve this using more simple methods. But you can achieve this very easily using collections.Counter.
from collections import Counter
def add(a, b):
return dict(Counter(a) + Counter(b))
Your professor probably wants something like this:
def add(a, b):
new_dict = copy of a
for each key/value pair in b
if key in new_dict
add value to value already present in new_dict
else
insert key/value pair into new_dict
return new_dict
You can try this:
def add(dict1, dict2):
return dict([(key,dict1[key]+dict2[key]) for key in dict1.keys()])
I personally like using a dictionary's get method for this kind of merge:
def add(a, b):
result = {}
for dictionary in (a, b):
for key, value in dictionary.items():
result[key] = result.get(key, 0) + value
return result

Python support sorted dictionary -- similar to C++ map?

I am using Python 2.7.x. I have a dictionary (I mean {}), key is int and value is string. I want to retrieve the key which has the minimal integer value. In C++, I think we can use map, which sort keys. And in Python, not sure if anything similar we can leverage? If my understanding is correct, Python dictionary (I mean {}) is not sorted by key.
thanks in advance,
Lin
Update
The OP has expressed a need for O(1) performance when finding the minimum key in a dictionary. Try the sortedcontainers module. It offers a SortedDict class:
>>> from sortedcontainers import SortedDict
>>> d = SortedDict({100: 'a', 27: 'b', 1234: 'c'})
>>> d.keys()
SortedSet([27, 100, 1234], key=None, load=1000)
>>> d.keys()[0]
27
>>> d[d.keys()[0]]
'b'
For a Python builtin dictionary you can use min(d) to find the lowest key:
>>> d = {100: 'a', 27: 'b', 1234: 'c'}
>>> print(d)
{1234: 'c', 27: 'b', 100: 'a'}
>>> print(min(d))
27
>>> print(d[min(d)])
b
In Python, dictionaries are represented internally by hash tables so you cannot natively get back the keys in sorted order. You can use sorted(d.keys()) to return a list of keys in sorted order. You can also use collections.OrderedDict if you pre-sort the keys. If you do not know the order of the keys ahead of time and you need to maintain the keys in sorted order as you insert values, you could take a look at this library for the SortedDict type.
There are binary tree implementations in Python:
https://pypi.python.org/pypi/bintrees/2.0.2
You can also use the blist.sorteddict.
You could also use the built-in bisect module to maintain a sorted list of tuples.
If all you care about is finding the minimum and you don't actually care about maintaining a sort, you can use the built-in heapq module to maintain a min-heap data structure which gives you constant time access to the minimal element.
Building off of #mhawke 's answer, you can simply do:
d = {1 : "a", 2 : "b", 3 : "c" }
print d[min(d.keys())] # => 'a'

Adding a dictionary item that is a dictionary itself

Consider following dictionary:
dict1 = {"A":{"B":"C"}}
print(dict1["A"]["B"])
This prints 'C'
I can now modify my dictionary like this
dict1["A"]["B"] = "D"
dict1["A"]["E"] = "F"
dict1["B"] = "G"
print(dict1)
And the output is
{'A': {'B': 'D', 'E': 'F'}, 'B': 'G'}
but I can't do this:
dict1["C"]["H"] = ["I"]
this however works:
dict2 = {"H":"I"}
dict1["C"] = dict2
print(dict1)
Output:
{'A': {'B': 'D', 'E': 'F'}, 'B': 'G', 'C': {'H': 'I'}}
Is there an alternative that doesn't require creating an additional dictionary?
I am just playing around to learn the language and not working on a concrete project.
Still, any help would be appreciated
Why would dict1["C"]["H"] = ["I"] work if there is no element "C" in dict1?
Do this:
dict1["C"] = {}
dict1["C"]["H"] = ["I"]
You haven't created the dictionary C yet. In python you must first create it before editing it. Like this:
dict1 = {}
dict1["C"] = {} # First create it before modifying it
dict1["C"]["H"] = ["I"]
print(dict1)
Let me explain.
foomain["C"] = 'blah'
sets it, but
foomain['C']['H'] = 'blah'
attempts to find foomain['C'], and fails. If it worked, it would then take that dictionary and use the assignment operator on it to assign blah to ['C']['H'].
It's like saying:
Okay, go find `foomain['C']`, then assign 'blah' to key 'H'
Instead of:
Okay, assign 'blah' to `foomain['C']['H']`
In other words, assignment and getting are entirely different in python.
dict1["C"]["H"] = ["I"]
In the above, the problem is that dict1["C"] has not been initialized, and so it is not known whether it is a dict and can be assigned a sub-element like ["H"] (or whether it is just, say, an integer and assigning to ["H"] would be an error).
There are packages to enable what you're talking about, and using a defaultdict would work for a single level of nestedness. But especially if you are starting out, it's probably better that you deal with such things explicitly.
val = dict1.get("C")
if isinstance(val, dict)
dict1["C"]["H"] = "I"
else
dict["C"] = {"H": "I"}
You can use setdefault which would assign default value in case of key error.
dict1.setdefault('C', {})['H'] = 'I'
Or you can altogether use defaultdict instead of dict.
from collections import defaultdict
There's a one-liner tree in https://gist.github.com/hrldcpr/2012250. It was posted as a cute hack, but works reasonably well.
from collections import defaultdict
def tree(): return defaultdict(tree)
users = tree()
users['harold']['username'] = 'hrldcpr'
users['handler']['username'] = 'matthandlersux'
Because of the defaultdict, each access on an undefined key generates a new value, and because of the recursion the new value will also be a new tree.

a(*{'q':'qqq'}),why only print key

def a(*x):
print x
a({'q':'qqq'})
a(*{'q':'qqq'})#why only print key.
traceback:
({'q': 'qqq'},)
('q',)
That's how dictionaries get converted to sequences.
tuple(dictionary) = tuple(dictionary.keys())
for a similar reason
for x in dictionary:
assigns keys, not pairs, to x
When you're calling a function, using an asterisk before a list or dict will pass it in as positional parameters.
For example:
>>> a(*('test', 'testing'))
('test', 'testing')
>>> a(*{'a': 'b', 'c': 'd'})
('a', 'c')
Using * in front of an expression in a function call iterates over the value of the expression (your dict, in this case) and makes each item in the iteration another parameter to the function invocation. Iterating over a dict in Python yields the keys (for better or worse).
Iterating a dictionary will yield its keys.
d = {'a': 1, 'b': 2, 'c': 3 }
for x in d:
print x # prints a, b, c but not necessarily in that order
sorted(d): # Gives a, b, c in that order. No 1/2/3.
If you want to get both keys and values from a dictionary, you can use .items() or .iteritems()
sorted(d.items()) # [('a,' 1), ('b', 2), ('c', 3)]
You are asking for a list of arguments, and then telling python to send a dict as a sequence of arguments. When a dict is converted to a sequence, it uses the keys.
I guess you are really looking for **, not *.
a(*{'q' : 'qqq'})
will try to expand your dict ({'q':'qqq'}) into an itemized list of arguments for the function.
Note that:
tuple({'q' : 'qqq'})
returns ('q',), which is exactly what you're seeing. When you coerce a dictionary to a list/tuple, you only get the list of keys.
Probably because that's what a dictionary returns when you do a standard iteration over it. It gets converted to a sequence containing it's keys. This example exhibits the same behaviour:
>>> for i in {"a": "1", "b": "2"}:
... print i
...
a
b
To get what I assume you expect you would pass it as variable keyword arguments instead, like this:
>>> def a(**kwargs):
... print kwargs
...
>>> a(**{"a": "1", "b": "2"})
{'a': '1', 'b': '2'}
Note that you are now basically back where you began and have gained nothing.

Categories

Resources