Python Dict inside List inside Dict - comprehension possible? - python

I have an object in Python 3 of this format:
a = {
'events': [
{
'timestamp': 123,
'message': 'test'
},
{
'timestamp': 456,
'message': 'foo'
},
{
'timestamp': 789,
'message': 'testbar'
},
],
'first': 'abc',
'last': 'def'
}
I want to create a new object of the same format, but filtered by whether the message key's corresponding value contains a certain string, for example filtering by "test":
a = {
'events': [
{
'timestamp': 123,
'message': 'test'
},
{
'timestamp': 789,
'message': 'testbar'
},
],
'first': 'abc',
'last': 'def'
}
Can I use a nested comprehension for this? I know you can do nested list comprehensions like:
[[y*2 for y in x] for x in l]
But is there a neat way for a dict > list > dict situation?

One option would be to create a new copy of the input dict without events, and then set the filtered events as you require, like this:
copy = {k: v for k, v in a.items() if k != 'events'}
copy['events'] = [e for e in a['events'] if 'test' in e['message']]
Or if you don't mind overwriting the original input, simply do this:
a['events'] = [e for e in a['events'] if 'test' in e['message']]

I would go with a list comprehension with an if-statement like the following:
[event for event in a["events"] if event["message"] == "test" ]
Loop through the values of the "events"-key and add them to the list if the value of their "message" key equals "test".
The result is a list of dictionaries that you can assign back to a["events"] or a copy of a if you would like to preserve a["events"].

So - you can use multiple layers of comprehension, but that doesn't mean you should. I think for such an example, you'd produce cleaner code, by running it through a couple of for loops. Having that said, I think the following is technically achieves the outcome you're asking for.
>>> pprint.pprint(a)
{'events': [{'message': 'test', 'timestamp': 123},
{'message': 'foo', 'timestamp': 456},
{'message': 'testbar', 'timestamp': 789}],
'first': 'abc',
'last': 'def'}
>>> aa = copy.deepcopy(a)
>>> aa['beta'] = aa['events']
>>> pprint.pprint({k:[item for item in v if 'test' in item['message']] if isinstance(v, list) else v for k, v in aa.items()})
{'beta': [{'message': 'test', 'timestamp': 123},
{'message': 'testbar', 'timestamp': 789}],
'events': [{'message': 'test', 'timestamp': 123},
{'message': 'testbar', 'timestamp': 789}],
'first': 'abc',
'last': 'def'}
>>> pprint.pprint({k:[item for item in v if 'test' in item['message']] if isinstance(v, list) else v for k, v in a.items()})
{'events': [{'message': 'test', 'timestamp': 123},
{'message': 'testbar', 'timestamp': 789}],
'first': 'abc',
'last': 'def'}
As said, this is something you can do; I would however on behalf of everyone who's had to read other people code in their careers, respectfully request that you don't use this in production code. A couple of for loops might be more LOC, but would in most cases be much more readable and maintainable.

Related

Creating json for special values in the dict of dicts python

I have dictionary like that:
dic={'61': {'NAME': 'John', 'LASTNAME': 'X', 'EMAIL': 'X#example.com', 'GRADE': '99'}, '52': {'NAME': 'Jennifer', 'LASTNAME': 'Y', 'EMAIL': 'Y#example.com', 'GRADE': '98'}}
obj = json.dumps(dic,indent=3)
print(obj)
I want to create Json for some values.
{
"NAME": "John",
"LASTNAME": "X",
,
"NAME": "Jennifer",
"LASTNAME": "Y"
}
Any idea for help?
If I understand correctly you want to keep the values of your original data without the indices and also filter out some of them (keep only "NAME" and "LASTNAME"). You can do so by using a combination of dictionary and list comprehensions:
array = [{k:v for k,v in d.items()if k in ("NAME","LASTNAME")} for d in dic.values()]
This creates the following output:
>>> array
[{'NAME': 'John', 'LASTNAME': 'X'}, {'NAME': 'Jennifer', 'LASTNAME': 'Y'}]

How can I remove nested keys and create a new dict and link both with an ID?

I have a problem. I have a dict my_Dict. This is somewhat nested. However, I would like to 'clean up' the dict my_Dict, by this I mean that I would like to separate all nested ones and also generate a unique ID so that I can later find the corresponding object again.
For example, I have detail: {...}, this nested, should later map an independent dict my_Detail_Dict and in addition, detail should receive a unique ID within my_Dict. Unfortunately, my list that I give out is empty. How can I remove my slaughtered keys and give them an ID?
my_Dict = {
'_key': '1',
'group': 'test',
'data': {},
'type': '',
'code': '007',
'conType': '1',
'flag': None,
'createdAt': '2021',
'currency': 'EUR',
'detail': {
'selector': {
'number': '12312',
'isTrue': True,
'requirements': [{
'type': 'customer',
'requirement': '1'}]
}
}
}
def nested_dict(my_Dict):
my_new_dict_list = []
for key in my_Dict.keys():
#print(f"Looking for {key}")
if isinstance(my_Dict[key], dict):
print(f"{key} is nested")
# Add id to nested stuff
my_Dict[key]["__id"] = 1
my_nested_Dict = my_Dict[key]
# Delete all nested from the key
del my_Dict[key]
# Add id to key, but not the nested stuff
my_Dict[key] = 1
my_new_dict_list.append(my_Dict[key])
my_new_dict_list.append(my_Dict)
return my_new_dict_list
nested_dict(my_Dict)
[OUT] []
# What I want
[my_Dict, my_Details_Dict, my_Data_Dict]
What I have
{'_key': '1',
'group': 'test',
'data': {},
'type': '',
'code': '007',
'conType': '1',
'flag': None,
'createdAt': '2021',
'currency': 'EUR',
'detail': {'selector': {'number': '12312',
'isTrue': True,
'requirements': [{'type': 'customer', 'requirement': '1'}]}}}
What I want
my_Dict = {'_key': '1',
'group': 'test',
'data': 18,
'type': '',
'code': '007',
'conType': '1',
'flag': None,
'createdAt': '2021',
'currency': 'EUR',
'detail': 22}
my_Data_Dict = {'__id': 18}
my_Detail_Dict = {'selector': {'number': '12312',
'isTrue': True,
'requirements': [{'type': 'customer', 'requirement': '1'}]}, '__id': 22}
The following code snippet will solve what you are trying to do:
my_Dict = {
'_key': '1',
'group': 'test',
'data': {},
'type': '',
'code': '007',
'conType': '1',
'flag': None,
'createdAt': '2021',
'currency': 'EUR',
'detail': {
'selector': {
'number': '12312',
'isTrue': True,
'requirements': [{
'type': 'customer',
'requirement': '1'}]
}
}
}
def nested_dict(my_Dict):
# Initializing a dictionary that will store all the nested dictionaries
my_new_dict = {}
idx = 0
for key in my_Dict.keys():
# Checking which keys are nested i.e are dictionaries
if isinstance(my_Dict[key], dict):
# Generating ID
idx += 1
# Adding generated ID as another key
my_Dict[key]["__id"] = idx
# Adding nested key with the ID to the new dictionary
my_new_dict[key] = my_Dict[key]
# Replacing nested key value with the generated ID
my_Dict[key] = idx
# Returning new dictionary containing all nested dictionaries with ID
return my_new_dict
result = nested_dict(my_Dict)
print(my_Dict)
# Iterating through dictionary to get all nested dictionaries
for item in result.items():
print(item)
If I understand you correctly, you wish to automatically make each nested dictionary it's own variable, and remove it from the main dictionary.
Finding the nested dictionaries and removing them from the main dictionary is not so difficult. However, automatically assigning them to a variable is not recommended for various reasons. Instead, what I would do is store all these dictionaries in a list, and then assign them manually to a variable.
# Prepare a list to store data in
inidividual_dicts = []
id_index = 1
for key in my_Dict.keys():
# For each key, we get the current value
value = my_Dict[key]
# Determine if the current value is a dictionary. If so, then it's a nested dict
if isinstance(value, dict):
print(key + " is a nested dict")
# Get the nested dictionary, and replace it with the ID
dict_value = my_Dict[key]
my_Dict[key] = id_index
# Add the id to previously nested dictionary
dict_value['__id'] = id_index
id_index = id_index + 1 # increase for next nested dic
inidividual_dicts.append(dict_value) # store it as a new dictionary
# Manually write out variables names, and assign the nested dictionaries to it.
[my_Details_Dict, my_Data_Dict] = inidividual_dicts

Add key and dictionary as key-value pair into existing dictionary

I'd like to add
"5f6c" as a key with values as photo_dict = {'caption': 'eb test', 'photo_id':'330da114-e41e-4cee-ba15-f9632'} into the below using python. I am not sure how to go about it
{'record': {'status': 'bad', 'form_id': '16bba1', 'project_id': None, 'form_values': {'5121':
'yes', '8339': 'ZTVPNG', '6cd3': '234624', '6b5b': '105626', 'e1f6': '[]', '5f6c': [{'id':
'f6efe67d7c5', 'created_at': '1614189636', 'updated_at': '1614189636', 'form_values': {'4ba6':
'Gaaaaah!'}}}
Such that the dictionary becomes
{'record': {'status': 'bad', 'form_id': '16bba1', 'project_id': None, 'form_values': {'5121':
'yes', '8339': 'ZTVPNG', '6cd3': '234624', '6b5b': '105626', 'e1f6': '[]', '5f6c': [{'id':
'f6efe67d7c5', 'created_at': '1614189636', 'updated_at': '1614189636', 'form_values': {'4ba6':
'Gaaaaah!', '5f6c': [{'caption': eb test, 'photo_id': '330da114-e41e-4cee-ba15-f9632'}]}}}
So it seems your initial dictionary is incorrectly formed and does not fully match what you are asking in the question, I would start by updating it with a correct format .
That being said, I ll assume the format would be close to this:
{
"record":{
"status":"bad",
"form_id":"16bba1",
"project_id":"None",
"form_values":{
"5121":"yes",
"8339":"ZTVPNG",
"6cd3":"234624",
"6b5b":"105626",
"e1f6":"[]",
"5f6c":[
{
"id":"f6efe67d7c5",
"created_at":"1614189636",
"updated_at":"1614189636",
"form_values":{
"4ba6":"Gaaaaah!",
"5f6c": {
"caption":"eb test",
"photo_id":"330da114-e41e-4cee-ba15-f9632"
}
}
}
]
}
}
}
You want to modify 5f6c with value
{'caption': 'eb test', 'photo_id':'330da114-e41e-4cee-ba15-f9632'}
Its not very clear which key you actually want to modify as the key 5f6c can be found in two different place in your dict, but it seems what you are trying to do is just modify a dictionary.
So what you need to take away from this is that to modify a dictionary and list you simply do
myDict[key] = value
myList[indice] = value
You contact as much as you want the operation in the same line.
If we take your example from above, and try to modify the most narrowed value, it would give us
import json
records = json.loads(theString)
records['record']['form_values']['5f6c'][0]['form_values']['5f6c'] = {"caption":"eb test", "photo_id":"330da114-e41e-4cee-ba15-f9632" }
After correcting your formatting I would do this.
my_dict = {'record': {'status': 'bad',
'form_id': '16bba1',
'project_id': None,
'form_values': {'5121': 'yes',
'8339': 'ZTVPNG',
'6cd3': '234624',
'6b5b': '105626',
'e1f6': '[]',
'5f6c': [{'id': 'f6efe67d7c5',
'created_at': '1614189636',
'updated_at': '1614189636',
'form_values': {'4ba6': 'Gaaaaah!'}
}
]}
}
}
photo_dict = {'caption': 'eb test', 'photo_id':'330da114-e41e-4cee-ba15-f9632'}
my_dict['record']['form_values']['5f6c'][0]['form_values']['5f6c'] = photo_dict
By the way you can get tools in your IDE that will help you with formatting and handle it for you.

Find item in a list of dictionaries

I have this data
data = [
{
'id': 'abcd738asdwe',
'name': 'John',
'mail': 'test#test.com',
},
{
'id': 'ieow83janx',
'name': 'Jane',
'mail': 'test#foobar.com',
}
]
The id's are unique, it's impossible that multiple dictonaries have the same id.
For example I want to get the item with the id "ieow83janx".
My current solution looks like this:
search_id = 'ieow83janx'
item = [x for x in data if x['id'] == search_id][0]
Do you think that's the be solution or does anyone know an alternative solution?
Since the ids are unique, you can store the items in a dictionary to achieve O(1) lookup.
lookup = {ele['id']: ele for ele in data}
then you can do
user_info = lookup[user_id]
to retrieve it
If you are going to get this kind of operations more than once on this particular object, I would recommend to translate it into a dictionary with id as a key.
data = [
{
'id': 'abcd738asdwe',
'name': 'John',
'mail': 'test#test.com',
},
{
'id': 'ieow83janx',
'name': 'Jane',
'mail': 'test#foobar.com',
}
]
data_dict = {item['id']: item for item in data}
#=> {'ieow83janx': {'mail': 'test#foobar.com', 'id': 'ieow83janx', 'name': 'Jane'}, 'abcd738asdwe': {'mail': 'test#test.com', 'id': 'abcd738asdwe', 'name': 'John'}}
data_dict['ieow83janx']
#=> {'mail': 'test#foobar.com', 'id': 'ieow83janx', 'name': 'Jane'}
In this case, this lookup operation will cost you some constant* O(1) time instead of O(N).
How about the next built-in function (docs):
>>> data = [
... {
... 'id': 'abcd738asdwe',
... 'name': 'John',
... 'mail': 'test#test.com',
... },
... {
... 'id': 'ieow83janx',
... 'name': 'Jane',
... 'mail': 'test#foobar.com',
... }
... ]
>>> search_id = 'ieow83janx'
>>> next(x for x in data if x['id'] == search_id)
{'id': 'ieow83janx', 'name': 'Jane', 'mail': 'test#foobar.com'}
EDIT:
It raises StopIteration if no match is found, which is a beautiful way to handle absence:
>>> search_id = 'does_not_exist'
>>> try:
... next(x for x in data if x['id'] == search_id)
... except StopIteration:
... print('Handled absence!')
...
Handled absence!
Without creating a new dictionary or without writing several lines of code, you can simply use the built-in filter function to get the item lazily, not checking after it finds the match.
next(filter(lambda d: d['id']==search_id, data))
should for just fine.
Would this not achieve your goal?
for i in data:
if i.get('id') == 'ieow83janx':
print(i)
(xenial)vash#localhost:~/python$ python3.7 split.py
{'id': 'ieow83janx', 'name': 'Jane', 'mail': 'test#foobar.com'}
Using comprehension:
[i for i in data if i.get('id') == 'ieow83janx']
if any(item['id']=='ieow83janx' for item in data):
#return item
As any function returns true if iterable (List of dictionaries in your case) has value present.
While using Generator Expression there will not be need of creating internal List. As there will not be duplicate values for the id in List of dictionaries, any will stop the iteration until the condition returns true. i.e the generator expression with any will stop iterating on shortcircuiting. Using List comprehension will create a entire List in the memory where as GE creates the element on the fly which will be better if you are having large items as it uses less memory.

compare two different length lists of dictionaries in python

I want to compare below dictionaries. Name key in the dictionary is common in both dictionaries.
If Name matched in both the dictionaries, i wanted to do some other stuff with the data.
PerfData = [
{'Name': 'abc', 'Type': 'Ex1', 'Access': 'N1', 'perfStatus':'Latest Perf', 'Comments': '07/12/2017 S/W Version'},
{'Name': 'xyz', 'Type': 'Ex1', 'Access': 'N2', 'perfStatus':'Latest Perf', 'Comments': '11/12/2017 S/W Version upgrade failed'},
{'Name': 'efg', 'Type': 'Cust1', 'Access': 'A1', 'perfStatus':'Old Perf', 'Comments': '11/10/2017 S/W Version upgrade failed, test data is active'}
]
beatData = [
{'Name': 'efg', 'Status': 'Latest', 'rcvd-timestamp': '1516756202.632'},
{'Name': 'abc', 'Status': 'Latest', 'rcvd-timestamp': '1516756202.896'}
]
Thanks
Rajeev
l = [{'name': 'abc'}, {'name': 'xyz'}]
k = [{'name': 'a'}, {'name': 'abc'}]
[i['name'] for i in l for f in k if i['name'] == f['name']]
Hope above logic work for you.
The answer provided didn't assign the result to any variable. If you want to print it, add the following would work:
result = [i['name'] for i in l for f in k if i['name'] == f['name']]
print(result)

Categories