Swap values in dictionary which contain list of dictionaries? - python

I have dictionary which contain list of dictionaries as below.
I want to swap all values of list of dictionary based on name.
Example: swap_function('Arvind','Jayesh') should swap other values like surname, fullname & email.
I have already tried a lot from other website's references but not able achieve my goal.
data = {
"items":[
{
"name":"Arvind",
"surname":"Patel",
"fullname":"Arvind Patel",
"email":"abc#xyx.com"
},
{
"name":"Jayesh",
"surname":"Patel",
"fullname":"Jayesh Patel",
"email":"Patel#gmail.com"
},
{
"name":"Krishna",
"surname":"dave",
"fullname":"Krishna dave",
"email":"Krishna#xyz.com"
},
{
"name":"Aditya",
"surname":"Patel",
"fullname":"Aditya Patel",
"email":"Aditya#abc.com"
}
]
}
I have tried like below but after that I am out of ideas.
def name_swap(name1, name2):
for key, item in data.items():
first_dict = item[0]
second_dict = item[1]
third_dict = item[2]
forth_dict = item[3]
fifth_dict = item[4]
after name_swap('Arvind', 'Krishna')
output :
data = {
"items":[
{
"name":"Arvind",
"surname":"dave",
"fullname":"Krishna dave",
"email":"Krishna#xyz.com"
},
{
"name":"Jayesh",
"surname":"Patel",
"fullname":"Jayesh Patel",
"email":"Patel#gmail.com"
},
{
"name":"Krishna",
"surname":"Patel",
"fullname":"Arvind Patel",
"email":"abc#xyx.com"
},
{
"name":"Aditya",
"surname":"Patel",
"fullname":"Aditya Patel",
"email":"Aditya#abc.com"
}
]
}

Try this code:
i = next(i for i,item in enumerate(data['items']) if item['name'] == 'Arvind')
j = next(i for i,item in enumerate(data['items']) if item['name'] == 'Krishna')
data['items'][i]['name'], data['items'][j]['name'] = 'Krishna', 'Arvind'
And gives:
{'items': [{'name': 'Arvind',
'surname': 'dave',
'fullname': 'Krishna dave',
'email': 'Krishna#xyz.com'},
{'name': 'Jayesh',
'surname': 'Patel',
'fullname': 'Jayesh Patel',
'email': 'Patel#gmail.com'},
{'name': 'Krishna',
'surname': 'Patel',
'fullname': 'Arvind Patel',
'email': 'abc#xyx.com'},
{'name': 'Aditya',
'surname': 'Patel',
'fullname': 'Aditya Patel',
'email': 'Aditya#abc.com'}]}
Ok now let's generalize this example, with the following function:
def swap_dict_list(dict_list, val1, val2, target='name', block_target=True):
try:
i = next(i for i,item in enumerate(dict_list) if item[target] == val1)
j = next(i for i,item in enumerate(dict_list) if item[target] == val2)
except StopIteration:
return dict_list
dict_list[i], dict_list[j] = dict_list[j], dict_list[i]
if block_target:
dict_list[i][target], dict_list[j][target] = val1, val2
return dict_list
In your case, you will use the function in this:
data['items'] = swap_dict_list(data['items'], 'Arvind', 'Krishna', target='name', block_target=True)
And you will get the same result shown above.
Code explenation
The swap_dict_list function receives as input the list of dictionaries 'dict_list', the 2 values ​​to search ('val1' and 'val2') and the dictionary key on which to perform the target search.
The function finds the indexes corresponding to the two values ​​searched for, and if they both exist it performs the swap. If block_target is True, the target values ​​are not exchanged.
The search is effected efficiently using generator expression.

Try this :
def switch(list, name1, name2):
for item in list:
if item['name'] == name1:
item['name'] = name2
elif item['name'] == name1:
item['name'] = name2
return list
data = {
"items":[
{
"name":"Arvind",
"surname":"Patel",
"fullname":"Arvind Patel",
"email":"abc#xyx.com"
},
{
"name":"Jayesh",
"surname":"Patel",
"fullname":"Jayesh Patel",
"email":"Patel#gmail.com"
},
{
"name":"Krishna",
"surname":"dave",
"fullname":"Krishna dave",
"email":"Krishna#xyz.com"
},
{
"name":"Aditya",
"surname":"Patel",
"fullname":"Aditya Patel",
"email":"Aditya#abc.com"
}
]
}
data['items'] = switch(data['items'], 'Arvind', 'Jayesh')
print data

If i got you right:
First you want to find the indexes of the two dicts:
Find the index of a dict within a list, by matching the dict's value
then switch positions of the items:
How to switch position of two items in a Python list?
def switch(list,name1, name2):
index1 = next((index for (index, d) in enumerate(list) if d["name"] == name1), None)
index2 = next((index for (index, d) in enumerate(list) if d["name"] == name2), None)
list[index1]['name'], list[index2]['name'] = list[index2]['name'], list[index1]['name']
return list
data['items'] = switch(data['items'], 'Arvind', 'Krishna')

Related

Comparing dictionary of list of dictionary/nested dictionary

There are two dict main and input, I want to validate the "input" such that all the keys in the list of dictionary and nested dictionary (if present/all keys are optional) matches that of the main if not the wrong/different key should be returned as the output.
main = "app":[{
"name": str,
"info": [
{
"role": str,
"scope": {"groups": list}
}
]
},{
"name": str,
"info": [
{"role": str}
]
}]
input_data = "app":[{
'name': 'nms',
'info': [
{
'role': 'user',
'scope': {'groups': ['xyz']
}
}]
},{
'name': 'abc',
'info': [
{'rol': 'user'}
]
}]
when compared input with main the wrong/different key should be given as output, in this case
['rol']
The schema module does exactly this.
You can catch SchemaUnexpectedTypeError to see which data doesn't match your pattern.
Also, make sure you don't use the word input as a variable name, as it's the name of a built-in function.
keys = []
def print_dict(d):
if type(d) == dict:
for val in d.keys():
df = d[val]
try:
if type(df) == list:
for i in range(0,len(df)):
if type(df[i]) == dict:
print_dict(df[i])
except AttributeError:
pass
keys.append(val)
else:
try:
x = d[0]
if type(x) == dict:
print_dict(d[0])
except:
pass
return keys
keys_input = print_dict(input)
keys = []
keys_main = print_dict(main)
print(keys_input)
print(keys_main)
for i in keys_input[:]:
if i in keys_main:
keys_input.remove(i)
print(keys_input)
This has worked for me. you can check above code snippet and if any changes provide more information so any chances if required.
Dictionary and lists compare theire content nested by default.
input_data == main should result in the right output if you format your dicts correctly. Try adding curly brackets "{"/"}" arround your dicts. It should probably look like something like this:
main = {"app": [{
"name": str,
"info": [
{
"role": str,
"scope": {"groups": list}
}
]
},{
"name": str,
"info": [
{"role": str}
]
}]}
input_data = {"app":[{
'name': 'nms',
'info': [
{
'role': 'user',
'scope': {'groups': ['xyz']
}
}]
},{
'name': 'abc',
'info': [
{'rol': 'user'}
]
}]}
input_data2 = {"app": [{
'name': 'nms',
'info': [
{
'role': 'user',
'scope': {'groups': ['xyz']
}
}]
}, {
'name': 'abc',
'info': [
{'rol': 'user'}
]
}]}
Comparision results should look like this:
input_data2 == input_data # True
main == input_data # False

Get max length of value inside a list which contains other lists

I got a list with keys and other lists. I want to create a function that checks the list for the longest value(string). It should give me back the longest string as number. I found nothing useful on the internet. only the strings with the key (value) need to be checked.
Output : It should count each character of the longest value(string).
Hope you can help me.
List:
[{'name': 'title', 'value': 'titel{TM} D3', 'is_on_label': 1},
{'name': 'DK in', 'value': '24V max 2.5A', 'is_on_label': 1,
'id_configuration': 79,
'options': [{'value': '30V max 3A', 'id_configuration_v': '1668'},
{'value': 'none', 'id_configuration_v': '1696'}]}]
function:
def checkLenFromConfigs(self, configs):
max_lenght = max(map(len, configs))
return max_lenght
You could recursively search for all values in your data structure:
data = [{
"name": "title",
"value": "titel{TM} D3",
"is_on_label": 1
},
[{
"name": "title",
"value": "titel{TM} D3",
"is_on_label": 1,
"sub_options": [
{
"value": "30V max 3A",
"id_configuration_v": "1668"
},
{
"value": "none none none none",
"id_configuration_v": "1696"
}
]
}],
{
"name": "DK in",
"value": "24V max 2.5A",
"is_on_label": 1,
"id_configuration": 79,
"options": [{
"value": "30V max 3A",
"id_configuration_v": "1668"
},
{
"value": "none",
"id_configuration_v": "1696"
}
]
}
]
def recur(data, count):
if isinstance(data, list):
for item in data:
count = recur(item, count)
elif isinstance(data, dict):
for k, v in data.items():
if k == 'value':
count.append(len(v))
else:
count = recur(v, count)
return count
result = recur(data, [])
print(max(result))
Out:
19

Merge dictionaries with same key from two lists of dicts in python

I have two dictionaries, as below. Both dictionaries have a list of dictionaries as the value associated with their properties key; each dictionary within these lists has an id key. I wish to merge my two dictionaries into one such that the properties list in the resulting dictionary only has one dictionary for each id.
{
"name":"harry",
"properties":[
{
"id":"N3",
"status":"OPEN",
"type":"energetic"
},
{
"id":"N5",
"status":"OPEN",
"type":"hot"
}
]
}
and the other list:
{
"name":"harry",
"properties":[
{
"id":"N3",
"type":"energetic",
"language": "english"
},
{
"id":"N6",
"status":"OPEN",
"type":"cool"
}
]
}
The output I am trying to achieve is:
"name":"harry",
"properties":[
{
"id":"N3",
"status":"OPEN",
"type":"energetic",
"language": "english"
},
{
"id":"N5",
"status":"OPEN",
"type":"hot"
},
{
"id":"N6",
"status":"OPEN",
"type":"cool"
}
]
}
As id: N3 is common in both the lists, those 2 dicts should be merged with all the fields. So far I have tried using itertools and
ds = [d1, d2]
d = {}
for k in d1.keys():
d[k] = tuple(d[k] for d in ds)
Could someone please help in figuring this out?
Here is one of the approach:
a = {
"name":"harry",
"properties":[
{
"id":"N3",
"status":"OPEN",
"type":"energetic"
},
{
"id":"N5",
"status":"OPEN",
"type":"hot"
}
]
}
b = {
"name":"harry",
"properties":[
{
"id":"N3",
"type":"energetic",
"language": "english"
},
{
"id":"N6",
"status":"OPEN",
"type":"cool"
}
]
}
# Create dic maintaining the index of each id in resp dict
a_ids = {item['id']: index for index,item in enumerate(a['properties'])} #{'N3': 0, 'N5': 1}
b_ids = {item['id']: index for index,item in enumerate(b['properties'])} #{'N3': 0, 'N6': 1}
# Loop through one of the dict created
for id in a_ids.keys():
# If same ID exists in another dict, update it with the key value
if id in b_ids:
b['properties'][b_ids[id]].update(a['properties'][a_ids[id]])
# If it does not exist, then just append the new dict
else:
b['properties'].append(a['properties'][a_ids[id]])
print (b)
Output:
{'name': 'harry', 'properties': [{'id': 'N3', 'type': 'energetic', 'language': 'english', 'status': 'OPEN'}, {'id': 'N6', 'status': 'OPEN', 'type': 'cool'}, {'id': 'N5', 'status': 'OPEN', 'type': 'hot'}]}
It might help to treat the two objects as elements each in their own lists. Maybe you have other objects with different name values, such as might come out of a JSON-formatted REST request.
Then you could do a left outer join on both name and id keys:
#!/usr/bin/env python
a = [
{
"name": "harry",
"properties": [
{
"id":"N3",
"status":"OPEN",
"type":"energetic"
},
{
"id":"N5",
"status":"OPEN",
"type":"hot"
}
]
}
]
b = [
{
"name": "harry",
"properties": [
{
"id":"N3",
"type":"energetic",
"language": "english"
},
{
"id":"N6",
"status":"OPEN",
"type":"cool"
}
]
}
]
a_names = set()
a_prop_ids_by_name = {}
a_by_name = {}
for ao in a:
an = ao['name']
a_names.add(an)
if an not in a_prop_ids_by_name:
a_prop_ids_by_name[an] = set()
for ap in ao['properties']:
api = ap['id']
a_prop_ids_by_name[an].add(api)
a_by_name[an] = ao
res = []
for bo in b:
bn = bo['name']
if bn not in a_names:
res.append(bo)
else:
ao = a_by_name[bn]
bp = bo['properties']
for bpo in bp:
if bpo['id'] not in a_prop_ids_by_name[bn]:
ao['properties'].append(bpo)
res.append(ao)
print(res)
The idea above is to process list a for names and ids. The names and ids-by-name are instances of a Python set. So members are always unique.
Once you have these sets, you can do the left outer join on the contents of list b.
Either there's an object in b that doesn't exist in a (i.e. shares a common name), in which case you add that object to the result as-is. But if there is an object in b that does exist in a (which shares a common name), then you iterate over that object's id values and look for ids not already in the a ids-by-name set. You add missing properties to a, and then add that processed object to the result.
Output:
[{'name': 'harry', 'properties': [{'id': 'N3', 'status': 'OPEN', 'type': 'energetic'}, {'id': 'N5', 'status': 'OPEN', 'type': 'hot'}, {'id': 'N6', 'status': 'OPEN', 'type': 'cool'}]}]
This doesn't do any error checking on input. This relies on name values being unique per object. So if you have duplicate keys in objects in both lists, you may get garbage (incorrect or unexpected output).

Set values for empty list within nested dictionaries

I have a cte query that returns me results of values that are linked (i.e. child -> parent).
Then in Python I am trying to create a nested dictionary that would represent something like this:
{
"name": "Child_example",
"parents": [
{
"name": "child_parent_1",
"parents": [{"name": "child_parent_1_parent", "parents": [{"name": "end", "parents": []}]}]
},
{
"name": "child_parent_2",
"parents": [{"name": "end", "parents": []}]
},
{
"name": "child_parent_3",
"parents": [{"name": "child_parent_3_parent", "parents": [{"name": "end", "parents": []}]}]
}
]
}
My input data looks something like so (it can have more data):
child_col
parent_col
name
depth
Child_example
child_parent_1_col
child_parent_1
0
Child_example
child_parent_2_col
child_parent_2
0
Child_example
child_parent_3_col
child_parent_3
0
child_parent_1_col
child_parent_1_parent
1_parent
1
child_parent_2_col
end
1_parent
1
child_parent_3_col
child_parent_3_parent
3_parent
1
child_parent_3_parent
end
end_3
2
child_parent_1_parent
end
end_1
2
However with my code so far:
r_dict = defaultdict(list)
depth_zero = [x for x in rows if x.depth == 0]
for row in depth_zero:
r_dict['name'] = row.path_key
r_dict['parents'].append({'name': row.path_parent_key, 'parents': []})
depth_not_zero = [x for x in rows if x.depth != 0]
# Set inner levels
for parent in r_dict['parents']:
name = parent['name']
inner_parent = parent['parents'].copy()
for row in depth_not_zero:
if row.path_key == name:
inner_parent.append({'name': row.path_parent_key, 'parents': []})
name = row.path_parent_key
parent['parents'] = inner_parent
I only manage to achieve to append it to initial "parents", instead of setting the ultimate nested "parents". I know it is to do with this line of code:
inner_parent.append({'name': row.path_parent_key, 'parents': []})
But I cannot work out how to essentially get and set it. Would this be a case for recursion instead of the way I am doing it?
Below is an example of the first nested dictionary output that I am currently creating with my code:
{
"name": "Child_example",
"parents": [
{
"name": "child_parent_1",
"parents": [
{"name": "child_parent_1", "parents": []}, {"name": "end", "parents": []}
]
}
]
}
I'm a bit baffled by the way you are assigning the "name" value: "Child_example" comes from child_col, "child_parent_1" from name, "child_parent_3_parent" from parent_col. So I simplified it a bit: I put in the second column of the child row the same value as in the first column of its parents rows. That said, if you really need to take the names from different columns it's just a matter of adding some ifs.
My proposal is to loop over the rows in reverse order, creating the inner dicts and then moving them into the outer ones:
rows = [["c1","p1c1",0],
["c1","p2c1",0],
["c1","p3c1",0],
["p1c1","p1p1c1",1],
["p2c1","end",1],
["p3c1","p1p3c1",1],
["p1p3c1","end",2],
["p1p1c1","end",2]]
r_dict = {}
for row in reversed(rows):
if row[1] == "end":
r_dict[row[0]] = {"name":row[0], "parents":[]}
else:
if not row[0] in r_dict:
r_dict[row[0]] = {"name":row[0], "parents":[]}
r_dict[row[0]]["parents"].append(r_dict[row[1]])
del r_dict[row[1]]
r_dict
{'c1': {'name': 'c1', 'parents': [{'name': 'p3c1', 'parents': [{'name': 'p1p3c1', 'parents': []}]}, {'name': 'p2c1', 'parents': []}, {'name': 'p1c1', 'parents': [{'name': 'p1p1c1', 'parents': []}]}]}}

How to perform quick upleveling in python?

I have the following object in python:
{
name: John,
age: {
years:18
},
computer_skills: {
years:4
},
mile_runner: {
years:2
}
}
I have an array with 100 people with the same structure.
What is the best way to go through all 100 people and make it such that there is no more "years"? In other words, each object in the 100 would look something like:
{
name: John,
age:18,
computer_skills:4,
mile_runner:2
}
I know I can do something in pseudocode:
for(item in list):
if('years' in (specific key)):
specifickey = item[(specific key)][(years)]
But is there a smarter/more efficent way?
Your pseudo-code is already pretty good I think:
for person in persons:
for k, v in person.items():
if isinstance(v, dict) and 'years' in v:
person[k] = v['years']
This overwrites every property which is a dictionary that has a years property with that property’s value.
Unlike other solutions (like dict comprehensions), this will modify the object in-place, so no new memory to keep everything is required.
def flatten(d):
ret = {}
for key, value in d.iteritems():
if isinstance(value, dict) and len(value) == 1 and "years" in value:
ret[key] = value["years"]
else:
ret[key] = value
return ret
d = {
"name": "John",
"age": {
"years":18
},
"computer_skills": {
"years":4
},
"mile_runner": {
"years":2
}
}
print flatten(d)
Result:
{'age': 18, 'mile_runner': 2, 'name': 'John', 'computer_skills': 4}
Dictionary comprehension:
import json
with open("input.json") as f:
cont = json.load(f)
print {el:cont[el]["years"] if "years" in cont[el] else cont[el] for el in cont}
prints
{u'age': 18, u'mile_runner': 2, u'name': u'John', u'computer_skills': 4}
where input.json contains
{
"name": "John",
"age": {
"years":18
},
"computer_skills": {
"years":4
},
"mile_runner": {
"years":2
}
}
Linear with regards to number of elements, you can't really hope for any lower.
As people said in the comments, it isn't exactly clear what your "object" is, but assuming that you actually have a list of dicts like this:
list = [{
'name': 'John',
'age': {
'years': 18
},
'computer_skills': {
'years':4
},
'mile_runner': {
'years':2
}
}]
Then you can do something like this:
for item in list:
for key in item:
try:
item[key] = item[key]['years']
except (TypeError, KeyError):
pass
Result:
list = [{'age': 18, 'mile_runner': 2, 'name': 'John', 'computer_skills': 4}]

Categories