I have a dictionary that looks like this:
{'items': [{'id': 1}, {'id': 2}, {'id': 3}]}
and I'm looking for a way to directly get the inner dictionary with id = 1.
Is there a way to reach this other than looping the list items and comparing the id?
first_with_id_or_none = \
next((value for value in dictionary['items'] if value['id'] == 1), None)
You will have to loop through the list. The good news is is that you can use a generator expression with next() to do that looping:
yourdict = next(d for d in somedict['items'] if d['id'] == 1)
This can raise a StopIteration exception if there is no such matching dictionary.
Use
yourdict = next((d for d in somedict['items'] if d['id'] == 1), None)
to return a default instead for that edge-case (here None is used, but pick what you need).
Make it into a function:
def get_inner_dict_with_value(D, key, value):
for k, v in D.items():
for d in v:
if d.get(key) == value:
return d
else:
raise ValueError('the dictionary was not found')
With explanation:
def get_inner_dict_with_value(D, key, value):
for k, v in D.items(): # loop the dictionary
# k = 'items'
# v = [{'id': 1}, {'id': 2}, {'id': 3}]
for d in v: # gets each inner dictionary
if d.get(key) == value: # find what you look for
return d # return it
else: # none of the inner dictionaries had what you wanted
raise ValueError('the dictionary was not found') # oh no!
Running it:
>>> get_inner_dict_with_value({'items': [{'id': 1}, {'id': 2}, {'id': 3}]}, 'id', 1)
{'id': 1}
Another method:
def get_inner_dict_with_value2(D, key, value):
try:
return next((d for l in D.values() for d in l if d.get(key) == value))
except StopIteration:
raise ValueError('the dictionary was not found')
>>> get_inner_dict_with_value2({'items': [{'id': 1}, {'id': 2}, {'id': 3}]}, 'id', 1)
{'id': 1}
Related
So I have this params:
p = [{'quantity': 1}, {'args': {'id': 12345678, 'age': 12}}]
And I want to be able to search for quantity and get the value 1 or the args key and get its doctionary ({'id': 12345678, 'age: 12})
This is what I have try:
def search(name: str, params: list[dict]):
try:
return next((x[name] for x in params if x), None)
except KeyError:
return None
I case I search for the value of quantity:
search(name='quantity', params=p)
This return 1
But in case I want to value of args:
search(name='args', params=p)
This return None
I have a set of functions that you could use for this [ getNestedVal(p, 'args') would return {'id': 12345678, 'age: 12} ]...or you can use this generator
def yieldNestedVals(obj, key):
if isinstance(obj, str): return
try: yield obj[key]
except: pass
if isinstance(obj, dict): obj = obj.values()
for i in (obj if hasattr(obj, '__iter__') else []):
for v in yieldNestedVals(i, key): yield v
inside search like
def search(name, params, defaultVal=None):
for v in yieldNestedVals(params, name): return v # return 1st generated value
return defaultVal # if nothing if generated
[ Ofc you can omit both the defaultVal parameter and the last line if you want to just return None when nothing if found, since that is the default behavior when a function doesn't come across a return statement. ]
Now search('args', p) should also return {'id': 12345678, 'age: 12} and you can try it with other keys as well:
# p = [{'quantity': 1}, {'args': {'id': 12345678, 'age': 12}}]
{k: search(k, p) for k in ['quantity', 'args', 'id', 'age', 'dneKey']}
would return
{'quantity': 1,
'args': {'id': 12345678, 'age': 12},
'id': 12345678,
'age': 12,
'dneKey': None}
Let's say I have a dictionary called my_dic:
my_dict = {'a': {'spam': {'foo': None, 'bar': None, 'baz': None},'eggs': None}, 'b': {'ham': None}}
Then if I input spam, it should return a, and if I input bar it should return spam. If I input b, it should return None. Basically getting the parent of the dictionary.
How would I go about doing this?
A simple recursive function, which returns the current key if needle in v is true; needle in v simply testing if the key exists in the associated value:
my_dict = {'a': {'spam': {'foo': None, 'bar': None, 'baz': None},'eggs': None}, 'b': {'ham': None}}
def get_parent_key(d: dict, needle: str):
for k, v in d.items():
if isinstance(v, dict):
if needle in v:
return k
if found := get_parent_key(v, needle):
return found
print(get_parent_key(my_dict, 'bar'))
I'm trying to make a function that would take nested array (dict/list in any order) and a key name as arguments and return all values of that key in a list.
my_key = "Items"
my_dict = [{'z': 0, 'x': 0, 'y': 0, 'Items': [{'Slot': 1, 'id': 'minecraft:rail', 'Count': 1}, {'Slot': 2, 'id': 'minecraft:white_shulker_box', 'tag': {'BlockEntityTag': {'id': 'minecraft:shulker_box', 'Items': [{'Slot': 0, 'Count': 1, 'tag': {'Items': [{'id': 'minecraft:amethyst_shard', 'Count': 1}]}, 'id': 'minecraft:bundle'}]}}, 'Count': 1}]}]
def recursive_lookup(data, key):
if isinstance(data, list):
for i in data:
recursive_lookup(i, key)
elif isinstance(data, dict):
for i, v in data.items():
if i == key:
print(f'{v = }')
if isinstance(v, list) or isinstance(v, dict): recursive_lookup(v, key)
print(recursive_lookup(my_dict, my_key))
Currently it prints out found items at print(f'{v = }'). How can I store those in a list and pass as a function return?
You can use .extend() to concatenate the result of recursive calls to a list.
def recursive_lookup(data, key):
values = []
if isinstance(data, list):
for i in data:
values.extend(recursive_lookup(i, key))
elif isinstance(data, dict):
for i, v in data.items():
if i == key:
values.append(v)
if isinstance(v, list) or isinstance(v, dict):
values.extend(recursive_lookup(v, key))
return values
You can what you want without any explicit recursion at all by making use of the json module in the standard library (assuming your data can be serialized into that format). This is because the JSON decoder supports an object_hook argument which is a function it will call everytime it encounters a dictionary.
The basic idea is to specify a function via this argument that merely "watches" what is being decoded and checks it for the sought-after key.
Here's what I mean:
import json
my_key = "Items"
my_dict = [{'z': 0, 'x': 0, 'y': 0, 'Items': [{'Slot': 1, 'id': 'minecraft:rail', 'Count': 1}, {'Slot': 2, 'id': 'minecraft:white_shulker_box', 'tag': {'BlockEntityTag': {'id': 'minecraft:shulker_box', 'Items': [{'Slot': 0, 'Count': 1, 'tag': {'Items': [{'id': 'minecraft:amethyst_shard', 'Count': 1}]}, 'id': 'minecraft:bundle'}]}}, 'Count': 1}]}]
def lookup(data, key):
results = []
def decode_dict(a_dict):
try:
results.append(a_dict[key])
except KeyError:
pass
return a_dict
json_repr = json.dumps(data) # Convert to JSON format.
json.loads(json_repr, object_hook=decode_dict) # Return value ignored.
return results
from pprint import pprint
pprint(lookup(my_dict, my_key), sort_dicts=False)
Pretty-printed result list:
[[{'id': 'minecraft:amethyst_shard', 'Count': 1}],
[{'Slot': 0,
'Count': 1,
'tag': {'Items': [{'id': 'minecraft:amethyst_shard', 'Count': 1}]},
'id': 'minecraft:bundle'}],
[{'Slot': 1, 'id': 'minecraft:rail', 'Count': 1},
{'Slot': 2,
'id': 'minecraft:white_shulker_box',
'tag': {'BlockEntityTag': {'id': 'minecraft:shulker_box',
'Items': [{'Slot': 0,
'Count': 1,
'tag': {'Items': [{'id': 'minecraft:amethyst_shard',
'Count': 1}]},
'id': 'minecraft:bundle'}]}},
'Count': 1}]]
You can keep a running list:
def recursive_lookup(data, key):
lst = []
if isinstance(data, list):
for i in data:
lst.append(recursive_lookup(i, key))
elif isinstance(data, dict):
for i, v in data.items():
if i == key:
lst.append([v])
if isinstance(v, list) or isinstance(v, dict): lst.append(recursive_lookup(v, key))
return lst
print(recursive_lookup(data, 'Items'))
I have a dict as follows.
dict = {'P': ['Demo'], 'Q': ['PMS']}
And I have a list of Dict as follows.
all = [{'Name': 'PMS'}, {'Name': 'Demo'}]
I need to have the dict's respective value in all as bellow.
new_list = [{'Name': 'PMS','Code': 'Q'}, {'Name': 'Demo','Code': 'P'}]
In this specific case, given that the values are just strings and therefore hashable objects, you can use a dictionary of reverse mappings. Be aware that it could not be used if the values were not hashable.
dct = {'P': ['Demo'], 'Q': ['PMS']}
all = [{'Name': 'PMS'}, {'Name': 'Demo'}]
reverse_mapping = {v[0]:k for k, v in dct.items()}
new_list = [d.copy() for d in all]
for d in new_list:
d['Code'] = reverse_mapping[d['Name']]
print(new_list)
This gives:
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]
dct = {'P': ['Demo'], 'Q': ['PMS']}
all_ = [{'Name': 'PMS'}, {'Name': 'Demo'}]
out = [dict(**l, Code=next(k for k, v in dct.items() if l['Name'] in v)) for l in all_]
print(out)
Prints:
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]
Or: you can make temporary dictionary:
tmp = {v[0]:k for k, v in dct.items()}
out = [dict(**l, Code=tmp[l['Name']]) for l in all_]
print(out)
You could make an inverted dictionary of codes, then go through the list of dictionaries and add the codes in:
codes = {"P": ["Demo"], "Q": ["PMS"]}
lst = [{"Name": "PMS"}, {"Name": "Demo"}]
inverted_codes = {value: key for key, values in codes.items() for value in values}
# {'Demo': 'P', 'PMS': 'Q'}
for dic in lst:
code = dic["Name"]
dic["Code"] = inverted_codes[code]
print(lst)
Output
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]
I've two nested dictionaries of variable depth orig_dict , new_dict
orig_dict = {"a":1, "b":[{"c":{"d":2}, "e":[{"q":12}, {"a":2}]}, {"h":[1,2,3,4]}], "e":{"we":12}}
new_dict = {"a":2, "b":[{"c":{"d":3}, "e":[{"q":120}, {"a":2}, {"x":10000}]}], "e":{"we":12}, "f":100}
Here new_dict is the updates needed to be done in orig_dict,
if key doesn't exists add that key-val to orig_dict
if key in orig_dict doesn't exists in new_dict then delete key-val from orig_dict
if key matches skip changes
I've code which will process only dictionaries as values but not list
import collections
def map_and_update(orig_dict, new_dict):
for key, val in new_dict.items():
if isinstance(val, collections.Mapping):
tmp = updatee(orig_dict.get(key, { }), val)
orig_dict[key] = tmp
else:
orig_dict[key] = new_dict[key]
return orig_dict
this will result:
{'a': 2, 'b': [{'c': {'d': 3}, 'e': [{'q': 120}, {'a': 2}, {'x': 10000}]}], 'e': {'we': 12}, 'f': 100}
but i expect
{'a': 1, 'b': [{'c': {'d': 2}, 'e': [{'q': 12}, {'a': 2}, {'x': 10000}]}], 'e': {'we': 12}, 'f': 100}
Note: Above i'm just copying the value of new_dict to orig_dict if i
see a list, but it should parse through list even and check whether
dict exists or not if exists then again do map
This code of mine is maybe not very readable, but at least it did finish the job. I dont have time right now, but if anyone want me to explain, i will edit this post later.
orig_dict = {"a":1, "b":[{"c":{"d":2}, "e":[{"q":12}, {"a":2}]}, {"h":[1,2,3,4]}], "e":{"we":12}}
new_dict = {"a":2, "b":[{"c":{"d":3}, "e":[{"q":120}, {"a":2}, {"x":10000}]}], "e":{"we":12}, "f":100}
for key, value in new_dict.items():
if isinstance(value, list):
for key1, value1 in new_dict['b'][0].items():
if orig_dict['b'][0].get(key1):
pass
if len(new_dict['b']) != len(orig_dict['b']):
del orig_dict['b'][len(new_dict['b']):]
length = len(new_dict['b'][0]['e'])
for i in range(length):
for k, v in new_dict['b'][0]['e'][i].items():
try:
if orig_dict['b'][0]['e'][i].get(k):
pass
else:
orig_dict['b'][0]['e'][i][k]=v
except:
orig_dict['b'][0]['e'].append({k:v})
elif orig_dict.get(key):
pass
else:
orig_dict[key]=value
print(orig_dict)