Convert list of lists with strings into list of dictionaries - python

I'm trying to convert a list of lists with strings like:
[
["amenity=language_school"],
["amenity=sport_school,place=rural", "amenity=sport_school,place=urban"],
["amenity=middle_school,place=city", "amenity=high_school,place=city"]
]
Some lists can have multiple string elements, and some of the string elements can have multiple key:values separated by a , like "amenity=high_school,place=city".
My goal is to get a list of dicts, in which the key of each dict could append in list several values from the same key. Like this:
[
{"amenity":"language_school"},
{"amenity":"sport_school", "place":["rural","urban"]},
{"amenity":["middle_school", "high_school"], "place":"city"}
]

This code works for you. Just if you want any list with just one member to become converted to a simple String it needs to add one line code to it.
Good wishes
output_list = []
for each_row in [
["amenity=language_school"],
["amenity=sport_school,place=rural", "amenity=sport_school,place=urban"],
["amenity=middle_school,place=city", "amenity=high_school,place=city"]
]:
output_list.append(dict())
for each_element in each_row:
for each_def in each_element.split(','):
key, value = each_def.split('=')
if key in output_list[-1]:
if value not in output_list[-1][key]:
output_list[-1][key].append(value)
else:
output_list[-1][key] = [value]
print(output_list)
The output:
[{'amenity': ['language_school']}, {'amenity': ['sport_school'], 'place': ['rural', 'urban']}, {'amenity': ['middle_school', 'high_school'], 'place': ['city']}]
And this is an alternative way with the same output:
output_list = []
for each_row in [
["amenity=language_school"],
["amenity=sport_school,place=rural", "amenity=sport_school,place=urban"],
["amenity=middle_school,place=city", "amenity=high_school,place=city"]
]:
output_list.append(dict())
for each_element in each_row:
for each_def in each_element.split(','):
key, value = each_def.split('=')
content = output_list[-1].get(key, [])
output_list[-1][key] = content + ([value] if value not in content else [])
print(output_list)

Related

How to combine every nth dict element in python list?

Input:
list1 = [
{
"dict_a":"dict_a_values"
},
{
"dict_b":"dict_b_values"
},
{
"dict_c":"dict_c_values"
},
{
"dict_d":"dict_d_values"
}
]
Assuming n=2, every two elements have to be combined together.
Output:
list1 = [
{
"dict_a":"dict_a_values",
"dict_c":"dict_c_values"
},
{
"dict_b":"dict_b_values",
"dict_d":"dict_d_values"
}
]
Ideally, it'd be nicer if the output could look like something as follows with an extra layer of nesting:
[
{"dict_combined_ac": {
"dict_a":"dict_a_values",
"dict_c":"dict_c_values"
}},
{"dict_combined_bd": {
"dict_b":"dict_b_values",
"dict_d":"dict_d_values"
}}
]
But since this is really difficult to implement, I'd be more than satisfied with an output looking something similar to the first example. Thanks in advance!
What I've tried so far:
[ ''.join(x) for x in zip(list1[0::2], list1[1::2]) ]
However, I know this doesn't work because I'm working with dict elements and not str elements and when wrapping the lists with str(), every two letters is being combined instead. I'm also unsure of how I can adjust this to be for every n elements instead of just 2.
Given the original list, as in the question, the following should generate the required output:
result_list = list()
n = 2 # number of elements you want in each partition
seen_idx = set()
for i in range(len(list1)): # iterate over all indices
if i not in seen_idx:
curr_idx_list = list() # current partition
for j in range(i, len(list1), n): # generate indices for a combination partition
seen_idx.add(j) # keep record of seen indices
curr_idx_list.append(j) # store indices for current partition
# At this point we have indices of a partition, now combine
temp_dict = dict() # temporary dictionary where we store combined values
for j in curr_idx_list: # iterate over indices of current partition
temp_dict.update(list1[j])
result_list.append(temp_dict) # add to result list
print(result_list, '\n')
# Bonus: change result list into list of nested dictionaries
new_res_list = list()
for elem in result_list: # for each (combined) dictionary in the list, we make new keys
key_names = list(elem.keys())
key_names = [e.split('_')[1] for e in key_names]
new_key = 'dict_combined_' + ''.join(key_names)
temp_dict = {new_key: elem}
new_res_list.append(temp_dict)
print(new_res_list, '\n')
The output is as follows:
[{'dict_a': 'dict_a_values', 'dict_c': 'dict_c_values'}, {'dict_b': 'dict_b_values', 'dict_d': 'dict_d_values'}]
[{'dict_combined_ac': {'dict_a': 'dict_a_values', 'dict_c': 'dict_c_values'}}, {'dict_combined_bd': {'dict_b': 'dict_b_values', 'dict_d': 'dict_d_values'}}]

Python 3: Creating list of multiple dictionaries have same keys but different values coming from multiple lists

I'm parsing through a response of XML using xpath from lxml library.
I'm getting the results and creating lists out of them like below:
object_name = [o.text for o in response.xpath('//*[name()="objectName"]')]
object_size_KB = [o.text for o in response.xpath('//*[name()="objectSize"]')]
I want to use the lists to create a dictionary per element in list and then add them to a final list like this:
[{'object_name': 'file1234', 'object_size_KB': 9347627},
{'object_name': 'file5671', 'objeobject_size_KBt_size': 9406875}]
I wanted a generator because I might need to search for more metadata from the response in the future so I want my code to be future proof and reduce repetition:
meta_names = {
'object_name': '//*[name()="objectName"]',
'object_size_KB': '//*[name()="objectSize"]'
}
def parse_response(response, meta_names):
"""
input: response: api xml response text from lxml xpath
input: meta_names: key names used to generate dictionary per object
return: list of objects dictionary
"""
mylist = []
# create list of each xpath match assign them to variables
for key, value in meta_names.items():
mylist.append({key: [o.text for o in response.xpath(value)]})
return mylist
However the function gives me this:
[{'object_name': ['file1234', 'file5671']}, {'object_size_KB': ['9347627', '9406875']}]
I've been searching for a similar case in the forums but couldn't find something to match my needs.
Appreciate your help.
UPDATE: Renneys answer was what I wanted I just adjusted the length value of range of my results since I don't always have the same length of xpath per object key and since my lists have identical length everytime I picked first index [0].
now the function looks like this.
def create_entries(root, keys):
tmp = []
for key in keys:
tmp.append([o.text for o in root.xpath('//*[name()="' + key + '"]')])
ret = []
# print(len(tmp[0]))
for i in range(len(tmp[0])):
add = {}
for j in range(len(keys)):
add[keys[j]] = tmp[j][i]
ret.append(add)
return ret
Use a two dimensional array:
def createEntries(root, keys):
tmp = []
for key in keys:
tmp.append([o.text for o in root.xpath('//*[name()="' + key + '"]')])
ret = []
for i in range(len(tmp)):
add = {}
for j in range(len(keys)):
add[keys[j]] = tmp[j][i]
ret.append(add)
return ret
I think this is what you are looking for.
You can use zip to combine your two lists into a list of value pairs.
Then, you can use a list comprehension or a generator expression to pair your value pairs with your desired keys.
import pprint
object_name = ['file1234', 'file5671']
object_size = [9347627, 9406875]
[{'object_name': 'file1234', 'object_size_KB': 9347627},
{'object_name': 'file5671', 'objeobject_size_KBt_size': 9406875}]
[{'object_name': ['file1234', 'file5671']}, {'object_size_KB': ['9347627', '9406875']}]
# List Comprehension
obj_list = [{'object_name': name, 'object_size': size} for name,size in zip(object_name,object_size)]
pprint.pprint(obj_list)
print('\n')
# Generator Expression
generator = ({'object_name': name, 'object_size': size} for name,size in zip(object_name,object_size))
for obj in generator:
print(obj)
Live Code Example -> https://onlinegdb.com/SyNSwd7jU
I think the accepted answer is more efficient, but here's an example of how list comprehensions could be used.
meta_names = {
'object_name': ['file1234', 'file5671'],
'object_size_KB': ['9347627', '9406875'],
'object_text': ['Bob', 'Ross']
}
def parse_response(meta_names):
"""
input: response: api xml response text from lxml xpath
input: meta_names: key names used to generate dictionary per object
return: list of objects dictionary
"""
# List comprehensions
to_dict = lambda l: [{key:val for key,val in pairs} for pairs in l]
objs = list(zip(*list([[key,val] for val in vals] for key,vals in meta_names.items())))
pprint.pprint(to_dict(objs))
parse_response(meta_names)
Live Code -> https://onlinegdb.com/ryLq4PVjL

python - how to append to a dictionary where the dictionary name is a variable

I have a list of dictionaries that I am iterating through. Each of the dictionaries has the same set of keys. I want to be able to append each item in the list to a new set of 7 dictionaries where the 7 dictionaries are each elements of a specific key.
For example if the dictionaries in the list has a key called 'houses' with values 'Dis' 'Brobnar', I want to have dictionares called 'Dis' and 'Brobnar' that will contain the entire dictionary.
Here's what I've tried, but it fails with an error message -
cards = [
{'house':'Dis','card_title':'Bob'},
{'house':'Brobnar','card_title':'Fred'},
]
Dis = {}
for i in cards:
if i['house'] == 'Dis':
i['house'].append(i)
error
i['house'].append(i)
AttributeError: 'str' object has no attribute 'append'
wanted results
Dis = [
{'house':'Dis','card_title':'Bob'},
]
Brobnar = [
{'house':'Brobnar','card_title':'Fred'},
]
Agree with #Selcuk's comment, so i am going to do an dictionary instead:
d={}.fromkeys([i['house'] for i in cards],[])
d={k:[i for i in cards if i['house']==k] for k,v in d.items()}
And now:
print(d)
Is:
{'Dis': [{'house': 'Dis', 'card_title': 'Bob'}], 'Brobnar': [{'house': 'Brobnar', 'card_title': 'Fred'}]}
If you already allocated your variable and just want to append to them, you need to create a wrapper dictionary to point to them:
cards = [
{'house':'Dis','card_title':'Bob'},
{'house':'Brobnar','card_title':'Fred'},
]
Dis = []
Brobnar = []
wrapper = {
'Dis':Dis,
'Brobnar':Brobnar,
}
for i in cards:
if not wrapper.get(i['house'], None) is None:
wrapper[i['house']].append(i)
print( Dis)
print( Brobnar)
#[{'house': 'Dis', 'card_title': 'Bob'}]
#[{'house': 'Brobnar', 'card_title': 'Fred'}]

Using a list of lists as a lookup table and updating a value in new list of lists

I have an application that creates a list of lists. The second element in the list needs to be assigned using lookup list which also consists of a list of lists.
I have used the "all" method to match the values in the list. If the list value exists in the lookup list, it should update the second position element in the new list. However this is not the case. The == comparative yields a False match for all elements, even though they all exist in both lists.
I have also tried various combinations of index finding commands but they are not able to unpack the values of each list.
My code is below. The goal is to replace the "xxx" values in the newData with the numbers in the lookupList.
lookupList= [['Garry','34'],['Simon', '24'] ,['Louise','13'] ]
newData = [['Louise','xxx'],['Garry', 'xxx'] ,['Simon','xxx'] ]
#Matching values
for i in newData:
if (all(i[0] == elem[0] for elem in lookupList)):
i[1] = elem[1]
You can't do what you want with all(), because elem is not a local variable outside of the generator expression.
Instead of using a list, use a dictionary to store the lookupList:
lookupDict = dict(lookupList)
and looking up matches is a simple constant-time (fast) lookup:
for entry in newData:
if entry[0] in lookupDict:
entry[1] = lookupDict[entry[0]]
you should use dictionaries instead, like this:
lookupList = newData = {}
old_lookupList = [['Garry','34'],['Simon', '24'] ,['Louise','13'] ]
old_newData = [['Louise','xxx'],['Garry', 'xxx'] ,['Simon','xxx'] ]
#convert into dictionary
for e in old_newData: newData[e[0]] = e[1]
for e in old_lookupList: lookupList[e[0]] = e[1]
#Matching values
for key in lookupList:
if key in newData.keys():
newData[key]=lookupList[key]
#convert into list
output_list = []
for x in newData:
output_list.append([x, newData[x]])
I like the following code since it can be tweaked and used in different ways:
lookupList= [ ['Garry', '34'],['Simon', '24'] ,['Louise', '13'] ]
newData = [ ['Louise', 'xxx'],['Garry', 'xxx'], ['Peter', 'xxx'] ,['Simon', 'xxx'] ]
#Matching values
for R in newData:
for i in range(0, len(lookupList) + 1):
try:
if lookupList[i][0] == R[0]:
R[1] = lookupList[i][1]
break
except:
print('Lookup fail on record:', R)
print(newData)

List of dicts: Getting list of matching dictionary based on id

I'm trying to get the matching IDs and store the data into one list. I have a list of dictionaries:
list = [
{'id':'123','name':'Jason','location': 'McHale'},
{'id':'432','name':'Tom','location': 'Sydney'},
{'id':'123','name':'Jason','location':'Tompson Hall'}
]
Expected output would be something like
# {'id':'123','name':'Jason','location': ['McHale', 'Tompson Hall']},
# {'id':'432','name':'Tom','location': 'Sydney'},
How can I get matching data based on dict ID value? I've tried:
for item in mylist:
list2 = []
row = any(list['id'] == list.id for id in list)
list2.append(row)
This doesn't work (it throws: TypeError: tuple indices must be integers or slices, not str). How can I get all items with the same ID and store into one dict?
First, you're iterating through the list of dictionaries in your for loop, but never referencing the dictionaries, which you're storing in item. I think when you wrote list[id] you mean item[id].
Second, any() returns a boolean (true or false), which isn't what you want. Instead, maybe try row = [dic for dic in list if dic['id'] == item['id']]
Third, if you define list2 within your for loop, it will go away every iteration. Move list2 = [] before the for loop.
That should give you a good start. Remember that row is just a list of all dictionaries that have the same id.
I would use kdopen's approach along with a merging method after converting the dictionary entries I expect to become lists into lists. Of course if you want to avoid redundancy then make them sets.
mylist = [
{'id':'123','name':['Jason'],'location': ['McHale']},
{'id':'432','name':['Tom'],'location': ['Sydney']},
{'id':'123','name':['Jason'],'location':['Tompson Hall']}
]
def merge(mylist,ID):
matches = [d for d in mylist if d['id']== ID]
shell = {'id':ID,'name':[],'location':[]}
for m in matches:
shell['name']+=m['name']
shell['location']+=m['location']
mylist.remove(m)
mylist.append(shell)
return mylist
updated_list = merge(mylist,'123')
Given this input
mylist = [
{'id':'123','name':'Jason','location': 'McHale'},
{'id':'432','name':'Tom','location': 'Sydney'},
{'id':'123','name':'Jason','location':'Tompson Hall'}
]
You can just extract it with a comprehension
matched = [d for d in mylist if d['id'] == '123']
Then you want to merge the locations. Assuming matched is not empty
final = matched[0]
final['location'] = [d['location'] for d in matched]
Here it is in the interpreter
In [1]: mylist = [
...: {'id':'123','name':'Jason','location': 'McHale'},
...: {'id':'432','name':'Tom','location': 'Sydney'},
...: {'id':'123','name':'Jason','location':'Tompson Hall'}
...: ]
In [2]: matched = [d for d in mylist if d['id'] == '123']
In [3]: final=matched[0]
In [4]: final['location'] = [d['location'] for d in matched]
In [5]: final
Out[5]: {'id': '123', 'location': ['McHale', 'Tompson Hall'], 'name': 'Jason'}
Obviously, you'd want to replace '123' with a variable holding the desired id value.
Wrapping it all up in a function:
def merge_all(df):
ids = {d['id'] for d in df}
result = []
for id in ids:
matches = [d for d in df if d['id'] == id]
combined = matches[0]
combined['location'] = [d['location'] for d in matches]
result.append(combined)
return result
Also, please don't use list as a variable name. It shadows the builtin list class.

Categories