Access dictionary values using list values as subsequent keys - python

keys = ['prop1', 'prop2', 'prop3']
dict = { prop1: { prop2: { prop3: True } } }
How do I get the value True out of the dict using the list?
Not having any success with
val = reduce((lambda a, b: dict[b]), keys)
update:
keys and dict can be arbitrarily long, but will always have matching properties/keys.

Using a loop:
>>> a = ['prop1', 'prop2', 'prop3']
>>> d = {'prop1': {'prop2': {'prop3': True}}}
>>> result = d
>>> for k in a:
... result = result[k]
...
>>> result
True
Using a functional style:
>>> from functools import reduce
>>> reduce(dict.get, a, d)
True

EDIT:
As Op. rephrased his question, I made an update:
Actually you don't need for keys at all to get "True".
You can use a recursive function to do it nicely without knowing the keys.
d = { 'prop1': { 'prop2': { 'prop3': True } } }
def d_c(dc):
if isinstance(list(dc.values())[0], dict):
return d_c(list(dc.values())[0])
return list(dc.values())[0]
Result:
True

Related

Pythonic way to map between two dicts with one having nested keys

I have dicts of two types representing same data. These are consumed by two different channels hence their keys are different.
for example:
Type A
{
"key1": "value1",
"key2": "value2",
"nestedKey1" : {
"key3" : "value3",
"key4" : "value4"
}
}
Type B
{
"equiKey1": "value1",
"equiKey2": "value2",
"equinestedKey1.key3" : "value3",
"equinestedKey1.key4" : "value4"
}
I want to map data from Type B to type A.
currently i am creating it as below
{
"key1": typeBObj.get("equiKey1"),
.....
}
Is there a better and faster way to do that in Python
First, you need a dictionary mapping keys in B to keys (or rather lists of keys) in A. (If the keys follow the pattern from your question, or a similar pattern, this dict might also be generated.)
B_to_A = {
"equiKey1": ["key1"],
"equiKey2": ["key2"],
"equinestedKey1.key3" : ["nestedKey1", "key3"],
"equinestedKey1.key4" : ["nestedKey1", "key4"]
}
Then you can define a function for translating those keys.
def map_B_to_A(d):
res = {}
for key, val in B.items():
r = res
*head, last = B_to_A[key]
for k in head:
r = res.setdefault(k, {})
r[last] = val
return res
print(map_B_to_A(B) == A) # True
Or a bit shorter, but probably less clear, using reduce:
def map_B_to_A(d):
res = {}
for key, val in B.items():
*head, last = B_to_A[key]
reduce(lambda d, k: d.setdefault(k, {}), head, res)[last] = val
return res

How can I construct a nested dictionary?

I have a list of strings, from which I have to construct a dict. So, for example, I have:
foo.bar:10
foo.hello.world:30
xyz.abc:40
pqr:100
This is represented as a dict:
{
"foo": {
"bar": 10,
"hello": {
"world": 30
}
},
"xyz": {
"abc": 40
},
"pqr": 100
}
This question is based on the same premise, but the answers discuss hardcoded depths such as:
mydict = ...
mydict['foo']['bar'] = 30
Since the dot seperated strings on the left may be of any depth, I can't figure out a way to build the dict. How should I parse the dot separated string and build the dict?
Building upon the solution in the links, you could
iterate over each line
for each line, extract a list of keys, and its value
recurse into a dictionary with each key using setdefault
assign the value at the bottom
lines = \
'''
foo.bar:10
foo.hello.world:30
xyz.abc:40
pqr:100
'''.splitlines()
d = {}
for l in lines:
k, v = l.split(':')
*f, l = k.split('.')
t = d
for k in f:
t = t.setdefault(k, {})
t[l] = int(v) # don't perform a conversion if your values aren't numeric
print(d)
{
"pqr": 100,
"foo": {
"bar": 10,
"hello": {
"world": 30
}
},
"xyz": {
"abc": 40
}
}
Recursive setdefault traversal learned from here.
Breaking down each step -
Split on :, extract the key-list string and the value
k, v = l.split(':')
Split the key-string on . to get a list of keys. I take the opportunity to partition the keys as well, so I have a separate reference to the last key that will be the key to v.
*f, l = k.split('.')
*f is the catch-all assignment, and f is a list of any number of values (possibly 0 values, if there's only one key in the key-string!)
For each key k in the key list f, recurse down into the "tree" using setdefault. This is similar to recursively traversing a linked list.
for k in f:
t = t.setdefault(k, {})
At the end, the last key value pair comes from l and v.
t[l] = v
What's wrong with incrementally building it?
mydict = {}
mydict["foo"] = {}
mydict["foo"]["bar"] = 30
mydict["foo"]["hello"] = {}
mydict["foo"]["hello"]["world"] = 30
mydict["foo"]["xyz"] = {}
mydict["foo"]["xyz"]["abc"] = 40
mydict["foo"]["pqr"] = 100
# ...
pprint.pprint(mydict) # {'foo': {'bar': 30, 'hello': {'world': 30}, 'pqr': 100, 'xyz': {'abc': 40}}}
Including the parsing, you could use something like this:
import pprint
inp = """foo.bar:10
foo.hello.world:30
xyz.abc:40
pqr:100
"""
mydict = {}
for line in inp.splitlines():
s, v = line.split(':')
parts = s.split(".")
d = mydict
for i in parts[:-1]:
if i not in d:
d[i] = {}
d = d[i]
d[parts[-1]] = v
pprint.pprint(mydict) # {'foo': {'bar': '10', 'hello': {'world': 30'}}, 'pqr': '100', 'xyz': {'abc': '40'}}
One key point to consider in your case is that you either want to create a dictionary in a parent's dictionarys value part or an integer
x = """
foo.bar:10
foo.hello.world:30
xyz.abc:40
pqr.a:100
"""
tree = {}
for item in x.split():
level, value = item.split(":")[0], item.split(":")[1]
t = tree
for part in item.split('.'):
keyval = part.split(":")
if len(keyval) > 1:
#integer
t = t.setdefault(keyval[0], keyval[1])
else:
t = t.setdefault(part, {})
import pprint
pprint.pprint(tree)
Result:
{'foo': {'bar': '10', 'hello': {'world': '30'}},
'pqr': {'a': '100'},
'xyz': {'abc': '40'}}

Python return with multiple dictionaries from a function

my problem is that I have two function and one of it's return value is two dictionaries in the following way:
def fnct1():
return dict1, dict2
so it's returning into my other function which return value is the two dictionaries from the previous function and also a new dictionary, so something like this
def fnct2():
return dict3, fnct(1)
the problem with this is that is has the following result:
({dict3},({dict1},{dict2})
but I want it to look the following way:
({dict3},{dict1},{dict2})
Since your function returns a tuple, you need to either return the individual tuple items, or unpack them:
def fnct1():
dict1 = { "name": "d1" }
dict2 = { "name": "d2" }
return dict1, dict2
def fnct2():
dict3 = { "name": "d3" }
res = fnct1()
return dict3, res[0], res[1] # return the individual tuple elements
# alternative implementation of fnct2:
def fnct2():
dict3 = { "name": "d3" }
d1, d2 = fnct1() # unpack your tuple
return dict3, d1, d2
print(fnct2())
# Output: ({'name': 'd3'}, {'name': 'd1'}, {'name': 'd2'})
You could unpack the values from fnct1() before returning them:
def fnct2():
dict1, dict2 = fnct1()
return dict3, dict1, dict2
If you say the returned value of the 1st function is a. In the 2nd just return a[0], a[1], otherDict.

How to remove dictionary's keys and values based on another dictionary?

I wish to remove keys and values in one JSON dictionary based on another JSON dictionary's keys and values. In a sense I am looking perform a "subtraction". Let's say I have JSON dictionaries a and b:
a = {
"my_app":
{
"environment_variables":
{
"SOME_ENV_VAR":
[
"/tmp",
"tmp2"
]
},
"variables":
{ "my_var": "1",
"my_other_var": "2"
}
}
}
b = {
"my_app":
{
"environment_variables":
{
"SOME_ENV_VAR":
[
"/tmp"
]
},
"variables":
{ "my_var": "1" }
}
}
Imagine you could do a-b=c where c looks like this:
c = {
"my_app":
{
"environment_variables":
{
"SOME_ENV_VAR":
[
"/tmp2"
]
},
"variables":
{ "my_other_var": "2" }
}
}
How can this be done?
You can loop through your dictionary using for key in dictionary: and you can delete keys using del dictionary[key], I think that's all you need. See the documentation for dictionaries: https://docs.python.org/2/tutorial/datastructures.html#dictionaries
The way you can do it is to:
Create copy of a -> c;
Iterate over every key, value pair inside b;
Check if for same top keys you have same inner keys and values and delete them from c;
Remove keys with empty values.
You should modify code, if your case will be somehow different (no dict(dict), etc).
print(A)
print(B)
C = A.copy()
# INFO: Suppose your max depth is as follows: "A = dict(key:dict(), ...)"
for k0, v0 in B.items():
# Look for similiar outer keys (check if 'vars' or 'env_vars' in A)
if k0 in C:
# Look for similiar inner (keys, values)
for k1, v1 in v0.items():
# If we have e.g. 'my_var' in B and in C and values are the same
if k1 in C[k0] and v1 == C[k0][k1]:
del C[k0][k1]
# Remove empty 'vars', 'env_vars'
if not C[k0]:
del C[k0]
print(C)
{'environment_variables': {'SOME_ENV_VAR': ['/tmp']},
'variables': {'my_var': '2', 'someones_var': '1'}}
{'environment_variables': {'SOME_ENV_VAR': ['/tmp']},
'variables': {'my_var': '2'}}
{'variables': {'someones_var': '1'}}
The following does what you need:
def subtract(a, b):
result = {}
for key, value in a.items():
if key not in b or b[key] != value:
if not isinstance(value, dict):
if isinstance(value, list):
result[key] = [item for item in value if item not in b[key]]
else:
result[key] = value
continue
inner_dict = subtract(value, b[key])
if len(inner_dict) > 0:
result[key] = inner_dict
return result
It checks if both key and value are present. It could del items, but I think is much better to return a new dict with the desired data instead of modifying the original.
c = subtract(a, b)
UPDATE
I have just updated for the latest version of the data provided by in the question. Now it 'subtract' list values as well.
UPDATE 2
Working example: ipython notebook

How do I find an item in an array of dictionaries?

Suppose I have this:
list = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
I need to find p2 and get its value.
You can try the following ... That will return all the values equivilant to the givenKey in all dictionaries.
ans = [d[key] for d in list if d.has_key(key)]
If this is what your actual code looks like (each key is unique), you should just use one dictionary:
things = { 'p1':'v1', 'p2':'v2', 'p3':'v3' }
do_something(things['p2'])
You can convert a list of dictionaries to one dictionary by merging them with update (but this will overwrite duplicate keys):
dict = {}
for item in list:
dict.update(item)
do_something(dict['p2'])
If that's not possible, you'll need to just loop through them:
for item in list:
if 'p2' in item:
do_something(item['p2'])
If you expect multiple results, you can also build up a list:
p2s = []
for item in list:
if 'p2' in item:
p2s.append(item['p2'])
Also, I wouldn't recommend actually naming any variables dict or list, since that will cause problems with the built-in dict() and list() functions.
These shouldn't be stored in a list to begin with, they should be stored in a dictionary. Since they're stored in a list, though, you can either search them as they are:
lst = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
p2 = next(d["p2"] for d in lst if "p2" in d)
Or turn them into a dictionary:
dct = {}
any(dct.update(d) for d in lst)
p2 = dct["p2"]
You can also use this one-liner:
filter(lambda x: 'p2' in x, list)[0]['p2']
if you have more than one 'p2', this will pick out the first; if you have none, it will raise IndexError.
for d in list:
if d.has_key("p2"):
return d['p2']
If it's a oneoff lookup, you can do something like this
>>> [i['p2'] for i in my_list if 'p2' in i]
['v2']
If you need to look up multiple keys, you should consider converting the list to something that can do key lookups in constant time (such as a dict)
>>> my_list = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
>>> my_dict = dict(i.popitem() for i in my_list)
>>> my_dict['p2']
'v2'
Start by flattening the list of dictionaries out to a dictionary, then you can index it by key and get the value:
{k:v for x in list for k,v in x.iteritems()}['p2']

Categories