I have some records like that in collection:
{
'_id': 1,
'test_field': [{'key1': 'value1'}, {'key2': 'value2'}]
}
test_field is a list of dicts. I need to push new dict in that list if any key does not exist and if it does I need to update that key’s value.
Examples:
{'key1': 'test_value'} → 'test_field': [{'key1': 'test_value'}, {'key2': 'value2'}]
{'test_key': 'test_value2'} → 'test_field': [{'key1': 'value1'}, {'key2': 'value2'}, {'test_key': 'test_value_2'}]
Help please
if you need a function in python to do it, this might work for you.
def modify_test_field(my_dict, test_field, new_key, new_val):
my_dict[test_field] = [obj for obj in my_dict[test_field] if new_key not in obj]
my_dict[test_field].append({new_key: new_val})
and call it like modify_test_field(orig_dict, 'test_field', new_key, new_val)
Related
Lets say I have a nested dictionary
nested_dict={"dict1":{"key1":"value1", "key2":"value2", "key3":"value3;value4"}}
Now I want to split value3 and value 4 under the same key like this,
nested_dict={"dict1":{"key1":"value1", "key2":"value2", 'key3': ['value3', 'value4']}}
What would be the best way to do so in Python?
use the fact that dict is mutable and you can recursively change anything under the sun :P
nested_dict={"dict1":{"key1":"value1", "key2":"value2", "key3":"value3;value4"}}
def sol(d):
for i in d:
if type(d[i]) is dict:
sol(d[i])
else:
d[i] = d[i].split(';')
if len(d[i])==1: d[i] = d[i][0]
sol(nested_dict)
print(nested_dict)
{'dict1': {'key1': 'value1', 'key2': 'value2', 'key3': ['value3', 'value4']}}
How would one go about cleaning a dictionary containing a variety of datatypes of nulls and empty lists, dicts etc. E.g.
raw = {'key': 'value', 'key1': [], 'key2': {}, 'key3': True, 'key4': None}
To:
refined = {'key': 'value', 'key3': true}
Because of the mixed nature of data types in the dictionary, using:
refined = {k:v for k,v in processed.items() if len(v)>0}
throws a
TypeError: object of type 'bool' has no len()
Is there a solution to make a second conditional based on type(v) is bool?
Edit: I've found the issue I was encountering employing solutions was a result of the structure of the data, asking a separate question to deal with that.
You can try this.
refined={k:v for k,v in raw.items() if v or isinstance(v,bool)}
raw={'key': 'value',
'key1': [],
'key2': {},
'key3': True,
'key4': None,
'key5': False}
refined={k:v for k,v in raw.items() if v or isinstance(v,bool)}
#{'key': 'value', 'key3': True, 'key5': False}
How about
refined = {k:v for k, v in processed.items() v is not None and (type(v) not in (list, dict) or len(v) > 0)}
I have a dictionary like this
dict1 = {'key1': 'value1', 'key2': 'value2'}
how do I have an array of the keys and values as dictionaries like this
array_of_dict_values = [{'key1': 'value1'}, {'key2': 'value2'}]
What would be the easiest way to accomplish this?
You can do this:
>>> aDict = {'key1': 'value1', 'key2': 'value2'}
>>> aList = [{k:v} for k, v in aDict.items()]
>>> aList
[{'key2': 'value2'}, {'key1': 'value1'}]
While somebody already answered with how to do this, I'm going to answer with "you probably don't want to do this." If every entry is a dictionary with a single key, wouldn't a list of key-value pairs work just as well?
dictionary = {'key1': 'value1', 'key2': 'value2'}
print(list(dictionary.items()))
# [('key2', 'value2'), ('key1', 'value1')]
I have a list of dictionaries and a function that can extract a value from each of those dictionaries in the list. The goal is that i get a dictionary where the keys are the values that are returned by the given function when i pass it the dictionaries from the given list of dictionaries. The according values in the returned dictionary should be the subset of dictionaries from the original list of dictionaries for which the given function returned the according key.
I know this explanation is very confusing, so I'm showing it in an implementation:
keygen = lambda x: x['key']
data = [{'key': 'key1',
'data': 'value2'},
{'key': 'key3',
'data': 'value2'},
{'key': 'key2',
'data': 'value2'},
{'key': 'key2',
'data': 'value2'},
{'key': 'key1',
'data': 'value2'}]
def merge_by_keygen(data, keygen):
return_value = {}
for dataset in data:
if keygen(dataset) not in return_value.keys():
return_value[keygen(dataset)] = []
return_value[keygen(dataset)].append(dataset)
return return_value
merge_by_keygen(data, keygen)
returns:
{'key3': [{'data': 'value2', 'key': 'key3'}],
'key2': [{'data': 'value2', 'key': 'key2'}, {'data': 'value2', 'key': 'key2'}],
'key1': [{'data': 'value2', 'key': 'key1'}, {'data': 'value2', 'key': 'key1'}]}
I'm looking for a nicer and more compact implementation of the same logic, like some dictionary/list comprehensions. Thanks!
This is an ideal problem to be handled by itertools.groupby
Implementation
from itertools import groupby
from operator import itemgetter
groups = groupby(sorted(data, key = itemgetter('key')), key = itemgetter('key'))
data_dict = {k : list(g) for k, g in groups}
or if you prefer one-liner
data_dict = {k : list(g)
for k, g in groupby(sorted(data,
key = itemgetter('key')),
key = itemgetter('key'))}
Output
{'key1': [{'data': 'value2', 'key': 'key1'},
{'data': 'value2', 'key': 'key1'}],
'key2': [{'data': 'value2', 'key': 'key2'},
{'data': 'value2', 'key': 'key2'}],
'key3': [{'data': 'value2', 'key': 'key3'}]}
If you don't mind using a third-party package, this is easily done with toolz.groupby:
>>> import toolz
>>> toolz.groupby(keygen, data)
{'key1': [{'data': 'value2', 'key': 'key1'},
{'data': 'value2', 'key': 'key1'}],
'key2': [{'data': 'value2', 'key': 'key2'},
{'data': 'value2', 'key': 'key2'}],
'key3': [{'data': 'value2', 'key': 'key3'}]}
The same result is also obtained with toolz.groupby('key', data)
I don't think this is amenable to a comprehension, but you can make it tidier using a collections.defaultdict(list) instance:
import collections
def merge_by_keygen(data, keygen):
return_value = collections.defaultdict(list)
for dataset in data:
key = keygen(dataset)
return_value[key].append(dataset)
return return_value
That looks pretty clean to me - you could mess around with ways to move where you call the keygen function if you like but I think you'd probably lose clarity.
I think this does it
return_value = {}
for d in data:
return_value.setdefault(keygen(d), []).append(d)
You can write it in a list comprehension, but it's ugly to use the side effects of a list comprehension to affect data and then build up a list of None results and throw it away...
r = {}
[r.setdefault(keygen(d), []).append(d) for d in data]
The core of your function all mashes down into the dictionary setdefault method. All three lines about calling the keygen, checking if the key is in the return dictionary, if it's not create an empty list, store the empty list in the dictionary, then get query the dictionary again to get the list ready to append to it - all done by setdefault().
I have the following dictionary:
my_dict = {'key1': {'key2': {'foo': 'bar'} } }
and I would like to append an entry to key1->key2->key3 with value 'blah' yielding:
my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': 'blah'} } }
I am looking for a generic solution that is independent of the number of keys, i.e. key1->key2->key3->key4->key5 should work as well, even though keys from key3 on downwards do not exist. So that I get:
my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': {'key4': {'key5': 'blah'} } } } }
Thanks in advance.
You can use the reduce() function to traverse a series of nested dictionaries:
def get_nested(d, path):
return reduce(dict.__getitem__, path, d)
Demo:
>>> def get_nested(d, path):
... return reduce(dict.__getitem__, path, d)
...
>>> my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': {'key4': {'key5': 'blah'}}}}}
>>> get_nested(my_dict, ('key1', 'key2', 'key3', 'key4', 'key5'))
'blah'
This version throws an exception when a key doesn't exist:
>>> get_nested(my_dict, ('key1', 'nonesuch'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in get_nested
KeyError: 'nonesuch'
but you could replace dict.__getitem__ with lambda d, k: d.setdefault(k, {}) to have it create empty dictionaries instead:
def get_nested_default(d, path):
return reduce(lambda d, k: d.setdefault(k, {}), path, d)
Demo:
>>> def get_nested_default(d, path):
... return reduce(lambda d, k: d.setdefault(k, {}), path, d)
...
>>> get_nested_default(my_dict, ('key1', 'nonesuch'))
{}
>>> my_dict
{'key1': {'key2': {'key3': {'key4': {'key5': 'blah'}}, 'foo': 'bar'}, 'nonesuch': {}}}
To set a value at a given path, traverse over all keys but the last one, then use the final key in a regular dictionary assignment:
def set_nested(d, path, value):
get_nested_default(d, path[:-1])[path[-1]] = value
This uses the get_nested_default() function to add empty dictionaries as needed:
>>> def set_nested(d, path, value):
... get_nested_default(d, path[:-1])[path[-1]] = value
...
>>> my_dict = {'key1': {'key2': {'foo': 'bar'}}}
>>> set_nested(my_dict, ('key1', 'key2', 'key3', 'key4', 'key5'), 'blah')
>>> my_dict
{'key1': {'key2': {'key3': {'key4': {'key5': 'blah'}}, 'foo': 'bar'}}}
An alternative to Martijn Pieters's excellent answer would be to use a nested defaultdict, rather than a regular dictionary:
from collections import defaultdict
nested = lambda: defaultdict(nested) # nested dictionary factory
my_dict = nested()
You can set values by using regular nested dictionary access semantics, and empty dictionaries will be created to fill the middle levels as necessary:
my_dict["key1"]["key2"]["key3"] = "blah"
This of course requires that the number of keys be known in advance when you write the code to set the value. If you want to be able to handle a variable-length list of keys, rather than a fixed number, you'll need functions to do the getting and setting for you, like in Martijn's answer.