Nested Dictionary check key value - python

I have a nested dictionary as shown below: (more than 2 keys "lots of lala's")
d={'lala': {'temp1': 'c', 'comp_b': 'bc', 'temp': 'b', 'comp_a': 'ac'}, 'lala1': {'temp1': 'c1', 'comp_b': 'bc1', 'temp': 'b1', 'comp_a': ''}
For all the parent keys in "a", I need to check if keys(comp_a and comp_b) have valid values.
In this case, "comp_a" of "lala1" doesn't have a value. So I need my function to only return "lala" as the output.
Parent keys to check=> a= ['lala','lala1']
Required values for keys=> compulsory= ['comp_b','comp_a']
Here's what I have so far:
def check_args(a,d):
compulsory=['comp_b','comp_a']
valid=[]
for a in d:
for elements in compulsory:
try:
if d.get(a,{}).get(elements) !='':
print "Valid"
except:
break
else:
print "Can't parse details of " + a + " as mandatory data missing "
continue
Question:
How do I return the valid parent keys i.e. "lala"?
Is there a better way to do what I've done so far?

Here is a clean way without try except
d={'lala': {'temp1': 'c', 'comp_b': 'bc', 'temp': 'b', 'comp_a': 'ac'}, 'lala1': {'temp1': 'c1', 'comp_b': 'bc1', 'temp': 'b1', 'comp_a': ''}}
compulsory= ['comp_b','comp_a']
ok_keys = [k for k,v in d.iteritems() if all([c in v and v[c]!='' for c in compulsory])]
ok_keys #<-- prints ['lala']
The logic all takes place in the list comprehension which first loops through the keys in d, and tests that all of the compulsory keys are within d[k] and that the d[k][c] values are not empty.

If I understood the question correctly you want to check for the dictionary values whether these have, for all compulsory elements a value?
There most certainly is a more elegant way to do this: you can use list comprehension:
e = {}
[key for key in a if all(d.get(key,e).get(c) for c in compulsory)]
The all(d.get(key,e).get(c) for c in compulsory) is crucial here since it is a filter condition. The all(..) will thus start enumerating over the compulsory list and for each element c, it will fetch that element and see if its truthiness is True. The empty string has a truthiness of False, so that won't work. If the key is not in the dictionary, then .get(c) will return None which has a truthiness of False as well.

Related

I need a dict-like structure with two keys, where you can get the list of all objects with a certain value of one of them

Let's say I have a dict that looks like this:
d['a']['1'] = 'foo'
d['a']['2'] = 'bar'
d['b']['1'] = 'baz'
d['b']['2'] = 'boo'
If I want to get every item where the first key is 'a', I can just do d['a'] and I will get all of them. However, what if I want to get all items where the second key is '1'? The only way I can think of is to make a second dictionary with a reverse order of the keys, which requires duplicating the contents. Is there a way to do this within a single structure?
Edit: forgot to mention: I want to do this without iterating over everything. I'm going to be dealing with dicts with hundreds of thousands of keys, so I need something scalable.
You're dealing with three dictionaries in this example: One with the values "foo" and "bar", one with the values "baz" and "boo", and an outer dictionary that maps the keys "a" and "b" to those first two inner dictionaries. You can iterate over the keys of both the outer and inner dictionaries with a nested for loop:
items = []
for outer_key in d:
for inner_key in d[outer_key]:
if inner_key == "1":
items.append(d[outer_key][inner_key])
break # No need to keep checking keys once you've found a match
If you don't care about the keys of the outer dictionary, you can also use d.values() to ignore the keys and just see the inner dictionaries, then do a direct membership check on those:
items = []
for inner_dict in d.values():
if "1" in inner_dict:
items.append(inner_dict["1"])
This can also be written as a list comprehension:
items = [inner_dict["1"] for inner_dict in d.values() if "1" in inner_dict]
What you want sounds very similar to a tree-structure which can be implemented as a dictionary-of-dictionaries. Here's a simple implement taken from one of the answers to the question What is the best way to implement nested dictionaries?:
class Tree(dict):
def __missing__(self, key):
value = self[key] = type(self)()
return value
def get_second_level(self, second_key):
found = []
for level2 in self.values():
if second_key in level2:
found.append(level2[second_key])
return found
d = Tree()
d['a']['1'] = 'foo'
d['a']['2'] = 'bar'
d['b']['1'] = 'baz'
d['b']['2'] = 'boo'
d['c']['2'] = 'mox'
d['c']['3'] = 'nix'
print(d) # -> {'a': {'1': 'foo', '2': 'bar'}, 'b': {'1': 'baz', '2': 'boo'},
# 'c': {'2': 'mox', '3': 'nix'}}
print(d['a']) # -> {'1': 'foo', '2': 'bar'}
print(d['a']['1']) # -> foo
print(d['a']['2']) # -> bar
print()
second_key = '1'
found = d.get_second_level(second_key)
print(f'Those with a second key of {second_key!r}') # -> Those with a second key of '1'
print(f' {found}') # -> ['foo', 'baz']
So after sleeping on it the solution I came up with was to make three dicts, the main one where the data is actually stored and identified by a tuple (d['a', '1'] = 'foo') and the other two are indexes that store all possible values of key B under key A where (A,B) is a valid combination (so a['a'] = ['1', '2'], b['1'] = ['a', 'b']. I don't entirely like this, since it still requires a hefty storage overhead and doesn't scale efficiently to higher numbers of keys, but it gets the job done without iterating and without duplicating the data. If anyone has a better idea, I'll be happy to hear it.

How values and keys get assigned in a dictionary using python

I was running this code through python tutor, and was just confused as to how the keys and values get switched around. I also was confused as to what value myDict[d[key]] would correspond to as I'm not sure what the d in [d[key]] actually does.
def dict_invert(d):
'''
d: dict
Returns an inverted dictionary according to the instructions above
'''
myDict = {}
for key in d.keys():
if d[key] in myDict:
myDict[d[key]].append(key)
else:
myDict[d[key]] = [key]
for val in myDict.values():
val.sort()
return myDict
print(dict_invert({8: 6, 2: 6, 4: 6, 6: 6}))
In your function d is the dictionary being passed in. Your code is creating a new dictionary, mapping the other direction (from the original dictionary's values to its keys). Since there may not be a one to one mapping (since values can be repeated in a dictionary), the new mapping actually goes from value to a list of keys.
When the code loops over the keys in d, it then uses d[key] to look up the corresponding value. As I commented above, this is not really the most efficient way to go about this. Instead of getting the key first and indexing to get the value, you can instead iterate over the items() of the dictionary and get key, value 2-tuples in the loop.
Here's how I'd rewrite the function, in what I think is a more clear fashion (as well as perhaps a little bit more efficient):
def dict_invert(d):
myDict = {}
for key, value in d.items(): # Get both key and value in the iteration.
if value in myDict: # That change makes these later lines more clear,
myDict[value].append(key) # as they can use value instead of d[key].
else:
myDict[value] = [key] # here too
for val in myDict.values():
val.sort()
return myDict
The function you are showing inverts a dictionary d. A dictionary is a collection of unique keys that map to values which are not necessarily unique. That means that when you swap keys and values, you may get multiple keys that have the same value. Your function handles this by adding keys in the input to a list in the inverse, instead of storing them directly as values. This avoids any possibility of conflict.
Let's look at a sample conceptually first before digging in. Let's say you have
d = {
'a': 1,
'b': 1,
'c': 2
}
When you invert that, you will have the keys 1 and 2. Key 1 will have two values: 'a' and 'b'. Key 2 will only have one value: 'c'. I used different types for the keys and values so you can tell immediately when you're looking at the input vs the output. The output should look like this:
myDict = {
1: ['a', 'b'],
2: ['c']
}
Now let's look at the code. First you initialize an empty output:
myDict = {}
Then you step through every key in the input d. Remember that these keys will become the values of the output:
for key in d.keys():
The value in d for key is d[key]. You need to check if that's a key in myDict since values become keys in the inverse:
if d[key] in myDict:
If the input's value is already a key in myDict, then it maps to a list of keys from d, and you need to append another one to the list. Specifically, d[key] represents the value in d for the key key. This value becomes a key in myDict, which is why it's being indexed like that:
myDict[d[key]].append(key)
Otherwise, create a new list with the single inverse recorded in it:
else:
myDict[d[key]] = [key]
The final step is to sort the values of the inverse. This is not necessarily a good idea. The values were keys in the input, so they are guaranteed to be hashable, but not necessarily comparable to each other:
for val in myDict.values():
val.sort()
The following should raise an error in Python 3:
dict_invert({(1, 2): 'a', 3: 'b'})
myDict[d[key]] takes value of d[key] and uses it as a key in myDict, for example
d = {'a': 'alpha', 'b': 'beta'}
D = {'alpha': 1, 'beta': 2}
D[d['a']] = 3
D[d['b']] = 4
now when contents of d and D should be as following
d = {'a': 'alpha', 'b': 'beta'}
D = {'alpha': 3, 'beta': 4}
d is the dictionary you are passing into the function
def dict_invert(d)
When you create
myDict[d[key]] = d
Its meaning is
myDict[value of d] = key of d
Resulting in
myDict = {'value of d': 'key of d'}

Iterating JSON structure of list object and finding next keys

I have a Json list and I want to print all the keys from a given key till the end of dictionary. But the code I wrote is very complex. How to do it with less complexity ?
I'm using Python 3
dictionary = [{"a": "1"}, {"b": "2"}, {"c": "3"}, {"d": "4"}]
try:
for token in dictionary:
if "b" in list(token.keys())[0]:
new_dict = dictionary[len(list(token.keys())[0]):]
for i in new_dict:
print(new_dict[len(list(i.keys())[0]):])
break
else:
print("Inception")
except Exception as error:
print(str(error))
DESIRED
Input: b
Output: c, d
My Output:
Inception
[{'c': '3'}, {'d': '4'}]
[{'c': '3'}, {'d': '4'}]
[{'c': '3'}, {'d': '4'}]
Use itertools.dropwhile() to skip all dictionaries that don't have a 'b' key:
from itertools import dropwhile
filtered = dropwhile(lambda t: 'b' not in t, dictionary)
next(filtered) # skip the dictionary with `b` in it.
for token in filtered:
print(token)
This prints all dictionaries after the first. If you only need to print their keys, do so explicitly:
filtered = dropwhile(lambda t: 'b' not in t, dictionary)
next(filtered) # skip the dictionary with `b` in it.
for token in filtered:
print(*token, sep='\n')
This prints the keys on separate lines; if there is just one key, then that's all that'll be printed for each token dictionary.)
As a side note: you really do not want to use list(dict.keys())[0]. Before Python 3.6, dictionaries have no set order (instead being subject to insertion and deletion history and the current random hash seed), so if you have more than one key what value you'll get is a gamble. And all you want to do is see if a key is present, so use a key in dictobject membership test.
To get the first key out of each dictionary, I'd use next(iter(dictobject)), avoiding creating a list:
first_key = next(iter(dictobject))
I'd only use this if I had single-key dictionaries. I'd also avoid using dictionaries for such a scenario; perhaps you really wanted to use an ordered dictionary instead (in Python < 3.6, use collections.OrderedDict(), otherwise use the regular dict type).
This is one way. The idea is to find the index of the dictionary with desired key. Then filter your list accordingly.
keys = [next(iter(x)) for x in dictionary]
res = keys[keys.index('b')+1:]
# ['c', 'd']
You can re-write your code as follows for your desired result.
found = False
for data in dictionary:
if found:
print(list(data.keys())[0])
if 'b' in data.keys():
found = True

Reaching into a nested dictionary several levels deep (that might not exist)

I have an API that I call that returns a dictionary. Part of that dictionary is itself another dictionary. In that inside dictionary, there are some keys that might not exist, or they might. Those keys could reference another dictionary.
To give an example, say I have the following dictionaries:
dict1 = {'a': {'b': {'c':{'d':3}}}}
dict2 = {'a': {'b': {''f': 2}}}
I would like to write a function that I can pass in the dictionary, and a list of keys that would lead me to the 3 in dict1, and the 2 in dict2. However, it is possible that b and c might not exist in dict1, and b and f might not exist in dict2.
I would like to have a function that I could call like this:
get_value(dict1, ['a', 'b', 'c'])
and that would return a 3, or if the keys are not found, then return a default value of 0.
I know that I can use something like this:
val = dict1.get('a', {}).get('b', {}).get('c', 0)
but that seems to be quite wordy to me.
I can also flatten the dict (see https://stackoverflow.com/a/6043835/1758023), but that can be a bit intensive since my dictionary is actually fairly large, and has about 5 levels of nesting in some keys. And, I only need to get two things from the dict.
Right now I am using the flattenDict function in the SO question, but that seems a bit of overkill for my situation.
You can use a recursive function:
def get_value(mydict, keys):
if not keys:
return mydict
if keys[0] not in mydict:
return 0
return get_value(mydict[keys[0]], keys[1:])
If keys can not only be missing, but be other, non-dict types, you can handle this like so:
def get_value(mydict, keys):
if not keys:
return mydict
key = keys[0]
try:
newdict = mydict[key]
except (TypeError, KeyError):
return 0
return get_value(newdict, keys[1:])
Without recursion, just iterate through the keys and go down one level at a time. Putting that inside a try/except allows you to handle the missing key case. KeyError will be raised when the key is not there, and TypeError will be raised if you hit the "bottom" of the dict too soon and try to apply the [] operator to an int or something.
def get_value(d, ks):
for k in ks:
try:
d = d[k] # descend one level
except (KeyError, TypeError):
return 0 # when any lookup fails, return 0
return d # return the final element
Here is a recursive function that should work for general cases
def recursive_get(d, k):
if len(k) == 0:
return 0
elif len(k) == 1:
return d.get(k[0], 0)
else:
value = d.get(k[0], 0)
if isinstance(value, dict):
return recursive_get(value, k[1:])
else:
return value
It takes arguments of the dict to search, and a list of keys, which it will check one per level
>>> dict1 = {'a': {'b': {'c':{'d':3}}}}
>>> recursive_get(dict1, ['a', 'b', 'c'])
{'d': 3}
>>> dict2 = {'a': {'b': {'f': 2}}}
>>> recursive_get(dict2, ['a', 'b', 'c'])
0

Update values for multiple keys in python

What is the cleanest way to update the values of multiple keys in a dictionary to the values stored in a tuple?
Example:
I want to go from
>>>mydict = {'a':None, 'b':None, 'c':None, 'd':None}
>>>mytuple = ('alpha', 'beta', 'delta')
to
>>>print mydict
{'a':'alpha', 'b':'beta', 'c':None, 'd':'delta'}
Is there an easy one-liner for this? Something like this seems to be getting close to what I want.
EDIT:
I don't wish to assign values to keys based on their first letter. I'm just hoping for something like
mydict('a','b','d') = mytuple
Obviously that doesn't work, but I'm hoping for something similar to that.
If you're trying to create a new dictionary:
d = dict(zip(keys, valuetuple))
If you're trying to add to an existing one, just change the = to .update(…).
So, your example can be written as:
mydict.update(dict(zip('abd', mytuple))))
If you're doing this more than once, I'd wrap it up in a function, so you can write:
setitems(d, ('a', 'b', 'd'), mytuple)
Or maybe a "curried" function that parallels operator.itemgetter?
You could use list comprehension to make it a one-liner, though it's not super efficient.
keys = [ 'a', 'b', 'c', 'd']
values = ['alpha', 'beta', 'delta']
dictionary = dict([(k,v) for k in keys for v in values if v.startswith(k)])
print dictionary #prints {'a': 'alpha', 'b': 'beta', 'd': 'delta'}
Assuming the association key <-> value is the first letter of the value is the first letter of the key.
dict( (v[0],v) for v in mytuple if v[0] in mydict)
I would avoid one-liners in this case. It makes code more readable.
for t in mytuple:
if t[0] in mydict.keys():
mydict[t[0]] = t
If you want to add mytuple items even if the key does not exist, simply remove the if statement.

Categories