Convert list of dictionaries into a nested dictionary [duplicate] - python

This question already has answers here:
Python: create a nested dictionary from a list of parent child values
(3 answers)
Closed 3 years ago.
I have a list of dictionaries that I got from the database in parent-child relationship:
data = [
{"id":1, "parent_id": 0, "name": "Wood", "price": 0},
{"id":2, "parent_id": 1, "name": "Mango", "price": 18},
{"id":3, "parent_id": 2, "name": "Table", "price": 342},
{"id":4, "parent_id": 2, "name": "Box", "price": 340},
{"id":5, "parent_id": 4, "name": "Pencil", "price": 240},
{"id":6, "parent_id": 0, "name": "Electronic", "price": 20},
{"id":7, "parent_id": 6, "name": "TV", "price": 350},
{"id":8, "parent_id": 6, "name": "Mobile", "price": 300},
{"id":9, "parent_id": 8, "name": "Iphone", "price": 0},
{"id":10, "parent_id": 9, "name": "Iphone 10", "price": 400}
]
I want to convert it to a nested dictionary such as
[ { "id": 1, "parent_id": 0, "name": "Wood", "price": 0, "children": [ { "id": 2, "parent_id": 1, "name": "Mango", "price": 18, "children": [ { "id": 3, "parent_id": 2, "name": "Table", "price": 342 }, { "id": 4, "parent_id": 2, "name": "Box", "price": 340, "children": [ { "id": 5, "parent_id": 4, "name": "Pencil", "price": 240 } ] } ] } ] }, { "id": 6, "parent_id": 0, "name": "Electronic", "price": 20, "children": [ { "id": 7, "parent_id": 6, "name": "TV", "price": 350 }, { "id": 8, "parent_id": 6, "name": "Mobile", "price": 300, "children": [ { "id": 9, "parent_id": 8, "name": "Iphone", "price": 0, "children": [ { "id": 10, "parent_id": 9, "name": "Iphone 10", "price": 400 } ] } ] } ] } ]

You can do this recursively, starting from the root nodes (where parent_id = 0) going downwards. But before your recursive calls, you can group nodes by their parent_id so that accessing them in each recursive call can be done in constant time:
levels = {}
for n in data:
levels.setdefault(n['parent_id'], []).append(n)
def build_tree(parent_id=0):
nodes = [dict(n) for n in levels.get(parent_id, [])]
for n in nodes:
children = build_tree(n['id'])
if children: n['children'] = children
return nodes
tree = build_tree()
print(tree)
Output
[{'id': 1, 'parent_id': 0, 'name': 'Wood', 'price': 0, 'children': [{'id': 2, 'parent_id': 1, 'name': 'Mango', 'price': 18, 'children': [{'id': 3, 'parent_id': 2, 'name': 'Table', 'price': 342}, {'id': 4, 'parent_id': 2, 'name': 'Box', 'price': 340, 'children': [{'id': 5, 'parent_id': 4, 'name': 'Pencil', 'price': 240}]}]}]}, {'id': 6, 'parent_id': 0, 'name': 'Electronic', 'price': 20, 'children': [{'id': 7, 'parent_id': 6, 'name': 'TV', 'price': 350}, {'id': 8, 'parent_id': 6, 'name': 'Mobile', 'price': 300, 'children': [{'id': 9, 'parent_id': 8, 'name': 'Iphone', 'price': 0,'children': [{'id': 10, 'parent_id': 9, 'name': 'Iphone 10', 'price': 400}]}]}]}]

Code is documented inline. Ignoring the corner cases like circular relations etc.
# Actual Data
data = [
{"id":1, "parent_id": 0, "name": "Wood", "price": 0},
{"id":2, "parent_id": 1, "name": "Mango", "price": 18},
{"id":3, "parent_id": 2, "name": "Table", "price": 342},
{"id":4, "parent_id": 2, "name": "Box", "price": 340},
{"id":5, "parent_id": 4, "name": "Pencil", "price": 240},
{"id":6, "parent_id": 0, "name": "Electronic", "price": 20},
{"id":7, "parent_id": 6, "name": "TV", "price": 350},
{"id":8, "parent_id": 6, "name": "Mobile", "price": 300},
{"id":9, "parent_id": 8, "name": "Iphone", "price": 0},
{"id":10, "parent_id": 9, "name": "Iphone 10", "price": 400}
]
# Create Parent -> child links using dictonary
data_dict = { r['id'] : r for r in data}
for r in data:
if r['parent_id'] in data_dict:
parent = data_dict[r['parent_id']]
if 'children' not in parent:
parent['children'] = []
parent['children'].append(r)
# Helper function to get all the id's associated with a parent
def get_all_ids(r):
l = list()
l.append(r['id'])
if 'children' in r:
for c in r['children']:
l.extend(get_all_ids(c))
return l
# Trimp the results to have a id only once
ids = set(data_dict.keys())
result = []
for r in data_dict.values():
the_ids = set(get_all_ids(r))
if ids.intersection(the_ids):
ids = ids.difference(the_ids)
result.append(r)
print (result)
Output:
[{'id': 1, 'parent_id': 0, 'name': 'Wood', 'price': 0, 'children': [{'id': 2, 'parent_id': 1, 'name': 'Mango', 'price': 18, 'children': [{'id': 3, 'parent_id': 2, 'name': 'Table', 'price': 342}, {'id': 4, 'parent_id': 2, 'name': 'Box', 'price': 340, 'children': [{'id': 5, 'parent_id': 4, 'name': 'Pencil', 'price': 240}]}]}]}, {'id': 6, 'parent_id': 0, 'name': 'Electronic', 'price': 20, 'children': [{'id': 7, 'parent_id': 6, 'name': 'TV', 'price': 350}, {'id': 8, 'parent_id': 6, 'name': 'Mobile', 'price': 300, 'children': [{'id': 9, 'parent_id': 8, 'name': 'Iphone', 'price': 0, 'children': [{'id': 10, 'parent_id': 9, 'name': 'Iphone 10', 'price': 400}]}]}]}]

I worked out a VERY SHORT solution, I believe it isn't the most efficient algorithm, but it does the job, will need a hell of optimization to work on very large data sets.
for i in range(len(data)-1, -1, -1):
data[i]["children"] = [child for child in data if child["parent_id"] == data[i]["id"]]
for child in data[i]["children"]:
data.remove(child)
Here is the complete explanation:
data = [
{"id":1, "parent_id": 0, "name": "Wood", "price": 0},
{"id":2, "parent_id": 1, "name": "Mango", "price": 18},
{"id":3, "parent_id": 2, "name": "Table", "price": 342},
{"id":4, "parent_id": 2, "name": "Box", "price": 340},
{"id":5, "parent_id": 4, "name": "Pencil", "price": 240},
{"id":6, "parent_id": 0, "name": "Electronic", "price": 20},
{"id":7, "parent_id": 6, "name": "TV", "price": 350},
{"id":8, "parent_id": 6, "name": "Mobile", "price": 300},
{"id":9, "parent_id": 8, "name": "Iphone", "price": 0},
{"id":10, "parent_id": 9, "name": "Iphone 10", "price": 400}
]
# Looping backwards,placing the lowest child
# into the next parent in the heirarchy
for i in range(len(data)-1, -1, -1):
# Create a dict key for the current parent in the loop called "children"
# and assign to it a list comprehension that loops over all items in the data
# to get the elements which have a parent_id equivalent to our current element's id
data[i]["children"] = [child for child in data if child["parent_id"] == data[i]["id"]]
# since the child is placed inside our its parent already, we will
# remove it from its actual position in the data
for child in data[i]["children"]:
data.remove(child)
# print the new data structure
print(data)
And here is the output:
[{'id': 1, 'parent_id': 0, 'name': 'Wood', 'price': 0, 'children': [{'id': 2, 'parent_id': 1, 'name': 'Mango', 'price': 18, 'children': [{'id': 3, 'parent_id': 2, 'name': 'Table', 'price': 342, 'children': []}, {'id': 4, 'parent_id': 2, 'name': 'Box', 'price': 340, 'children': [{'id': 5, 'parent_id': 4, 'name': 'Pencil', 'price': 240, 'children': []}]}]}]}, {'id': 6, 'parent_id': 0, 'name': 'Electronic', 'price': 20, 'children': [{'id': 7, 'parent_id': 6, 'name': 'TV', 'price': 350, 'children': []}, {'id': 8, 'parent_id': 6, 'name': 'Mobile', 'price': 300, 'children': [{'id': 9, 'parent_id': 8, 'name': 'Iphone', 'price': 0, 'children': [{'id': 10, 'parent_id': 9, 'name': 'Iphone 10', 'price': 400, 'children': []}]}]}]}]

Related

append() is overwriting the previous data he has written instead of just adding at the end

i'm wondering why the function append() is overwritting the elements given in a list that he has to write one after another.
I'm really lost here.
So here is the list of elements (I use fake identity names)
list = [{'Objet': 'premier', 'ne pas ': 'supprimer', "don't": 'delete'}, {'name': 'Bridoux', 'first_name': 'Justin', 'birthday': '14/05/2000', 'note': 'aucune', 'player_id': 1, 'total_score': 0, 'tournament_score': 0}, {'name': 'Clerico', 'first_name': 'Leonard', 'birthday': '07/08/1969', 'note': 'aucune', 'player_id': 2, 'total_score': 0, 'tournament_score': 0}, {'name': 'Boutroux', 'first_name': 'Yvon', 'birthday': '29/04/1984', 'note': 'aucune', 'player_id': 3, 'total_score': 0, 'tournament_score': 0}, {'name': 'Badeaux', 'first_name': 'Georges', 'birthday': '17/02/1962', 'note': 'aucune', 'player_id': 4, 'total_score': 0, 'tournament_score': 0}, {'name': 'Lajoie', 'first_name': 'Etienne', 'birthday': '16/06/1978', 'note': 'aucune', 'player_id': 5, 'total_score': 0, 'tournament_score': 0}, {'name': 'Foucher', 'first_name': 'Vincent', 'birthday': '08/01/1990', 'note': 'aucune', 'player_id': 6, 'total_score': 0, 'tournament_score': 0}, {'name': 'Coordonnier', 'first_name': 'Pierre', 'birthday': '20/10/1992', 'note': 'aucune', 'player_id': 7, 'total_score': 0, 'tournament_score': 0}, {'name': 'Chaucard', 'first_name': 'Alex', 'birthday': '10/07/2001', 'note': 'aucune', 'player_id': 8, 'total_score': 0, 'tournament_score': 0}, {'name': 'Marchand', 'first_name': 'Enzo', 'birthday': '06/06/1995', 'note': 'aucune', 'player_id': 9, 'total_score': 0, 'tournament_score': 0}, {'name': 'Flantier', 'first_name': 'Noel', 'birthday': '25/12/1960', 'note': 'OSS 117', 'player_id': 10, 'total_score': 0, 'tournament_score': 0}]
And here is the code that iterate over the list of dictionnaries to get only names, first_name, and player_id.
data = [["Nom", "Prénom", "ID Joueur"]]
data_row = []
for player in range(len(list)):
for key in list[player]:
if key == 'name':
data_row.append(list[player]['name'])
elif key == 'first_name':
data_row.append(list[player]['first_name'])
elif key == 'player_id':
data_row.append(list[player]['player_id'])
print(data_row)
data.append(data_row)
print(data)
data_row.clear()
I should have a list of lists with 3 attributes in each of them(name, first name, and player id).
But has you can see it's overwriting the previous list that has been given instead of just adding at the end of the list.
['Bridoux', 'Justin', 1]
[['Nom', 'Prénom', 'ID Joueur'], ['Bridoux', 'Justin', 1]]
['Clerico', 'Leonard', 2]
[['Nom', 'Prénom', 'ID Joueur'], ['Clerico', 'Leonard', 2], ['Clerico', 'Leonard', 2]]
['Boutroux', 'Yvon', 3]
[['Nom', 'Prénom', 'ID Joueur'], ['Boutroux', 'Yvon', 3], ['Boutroux', 'Yvon', 3], ['Boutroux', 'Yvon', 3]]
I've tryied to do it with data += data_row but then it's not putting the [] so I no longer have lists inside.
Why does it work like this ? and it's not removing the first element that as been declared at first as you can see.
The expected result would be :
[['Nom', 'Prénom', 'ID Joueur'], ['Bridoux', 'Justin', 1], ['Clerico', 'Leonard', 2], ['Boutroux', 'Yvon', 3]]
Try:
lst = [
{"Objet": "premier", "ne pas ": "supprimer", "don't": "delete"},
{
"name": "Bridoux",
"first_name": "Justin",
"birthday": "14/05/2000",
"note": "aucune",
"player_id": 1,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Clerico",
"first_name": "Leonard",
"birthday": "07/08/1969",
"note": "aucune",
"player_id": 2,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Boutroux",
"first_name": "Yvon",
"birthday": "29/04/1984",
"note": "aucune",
"player_id": 3,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Badeaux",
"first_name": "Georges",
"birthday": "17/02/1962",
"note": "aucune",
"player_id": 4,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Lajoie",
"first_name": "Etienne",
"birthday": "16/06/1978",
"note": "aucune",
"player_id": 5,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Foucher",
"first_name": "Vincent",
"birthday": "08/01/1990",
"note": "aucune",
"player_id": 6,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Coordonnier",
"first_name": "Pierre",
"birthday": "20/10/1992",
"note": "aucune",
"player_id": 7,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Chaucard",
"first_name": "Alex",
"birthday": "10/07/2001",
"note": "aucune",
"player_id": 8,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Marchand",
"first_name": "Enzo",
"birthday": "06/06/1995",
"note": "aucune",
"player_id": 9,
"total_score": 0,
"tournament_score": 0,
},
{
"name": "Flantier",
"first_name": "Noel",
"birthday": "25/12/1960",
"note": "OSS 117",
"player_id": 10,
"total_score": 0,
"tournament_score": 0,
},
]
data = [["Nom", "Prénom", "ID Joueur"]]
for d in lst[1:]:
data.append([d["name"], d["first_name"], d["player_id"]])
print(data)
Prints:
[
["Nom", "Prénom", "ID Joueur"],
["Bridoux", "Justin", 1],
["Clerico", "Leonard", 2],
["Boutroux", "Yvon", 3],
["Badeaux", "Georges", 4],
["Lajoie", "Etienne", 5],
["Foucher", "Vincent", 6],
["Coordonnier", "Pierre", 7],
["Chaucard", "Alex", 8],
["Marchand", "Enzo", 9],
["Flantier", "Noel", 10],
]
you can try using
list=[{'name': 'Clerico', 'first_name': 'Leonard', 'birthday': '07/08/1969', 'note': 'aucune', 'player_id': 2, 'total_score': 0, 'tournament_score': 0}, {'name': 'Boutroux', 'first_name': 'Yvon', 'birthday': '29/04/1984', 'note': 'aucune', 'player_id': 3, 'total_score': 0, 'tournament_score': 0}, {'name': 'Badeaux', 'first_name': 'Georges', 'birthday': '17/02/1962', 'note': 'aucune', 'player_id': 4, 'total_score': 0, 'tournament_score': 0}, {'name': 'Lajoie', 'first_name': 'Etienne', 'birthday': '16/06/1978', 'note': 'aucune', 'player_id': 5, 'total_score': 0, 'tournament_score': 0}, {'name': 'Foucher', 'first_name': 'Vincent', 'birthday': '08/01/1990', 'note': 'aucune', 'player_id': 6, 'total_score': 0, 'tournament_score': 0}, {'name': 'Coordonnier', 'first_name': 'Pierre', 'birthday': '20/10/1992', 'note': 'aucune', 'player_id': 7, 'total_score': 0, 'tournament_score': 0}, {'name': 'Chaucard', 'first_name': 'Alex', 'birthday': '10/07/2001', 'note': 'aucune', 'player_id': 8, 'total_score': 0, 'tournament_score': 0}, {'name': 'Marchand', 'first_name': 'Enzo', 'birthday': '06/06/1995', 'note': 'aucune', 'player_id': 9, 'total_score': 0, 'tournament_score': 0}, {'name': 'Flantier', 'first_name': 'Noel', 'birthday': '25/12/1960', 'note': 'OSS 117', 'player_id': 10, 'total_score': 0, 'tournament_score': 0}]
data = [["Nom", "Prénom", "ID Joueur"]]
data_row = []
for player in range(len(list)):
for key in list[player]:
if key == 'name':
data_row.append(list[player]['name'])
elif key == 'first_name':
data_row.append(list[player]['first_name'])
elif key == 'player_id':
data_row.append(list[player]['player_id'])
print(data_row)
a=data_row.copy()
data+=[a,]
print(data)
data_row.clear()
It is because list is also getting dynamically updated when you clear data_row . So, it is required to make a soft copy which won't be changed with changes in data_row.
Hope it helps✌️

How to convert list of multiple jsons into individual jsons using pyspark

I have a list of jsons as mentioned below
[
{
"files": 0,
"data": [
{"name": "RFC", "value": "XXXXXXX", "attId": 01},
{"name": "NOMBRE", "value": "JOSE", "attId": 02},
{"name": "APELLIDO PATERNO", "value": "MONTIEL", "attId": 03},
{"name": "APELLIDO MATERNO", "value": "MENDOZA", "attId": 04},
{"name": "FECHA NACIMIENTO", "value": "1989-02-04", "attId": 05}
],
"dirId": 1,
"docId": 4,
"structure": {
"name": "personales",
"folioId": 22
}
},
{
"files": 0,
"data": [
{"name": "CALLE", "value": "AMOR", "attId": 06},
{"name": "No. EXTERIOR", "value": "4", "attId": 07},
{"name": "No. INTERIOR", "value": "2", "attId": 08},
{"name": "C.P.", "value": "55060", "attId": 09},
{"name": "ENTIDAD", "value": "ESTADO DE MEXICO", "attId": 10},
{"name": "MUNICIPIO", "value": "ECATEPEC", "attId": 11},
{"name": "COLONIA", "value": "INDUSTRIAL", "attId": 12}
],
"dirId": 1,
"docId": 4,
"structure": {
"name": "direccion",
"folioId": 22
}
}
]
I need to convert this list of jsons into separate individual jsons and execute them separately.
how to achieve this using pyspark or python?
Something like this, just one loop should work;
PN: There are few leading zeros in your input raw data...
# Assuming given list in your question is stored in lst
for item in lst:
print(item,"\n")
# item here will behave as individual dict/json
# Do your coding here as required
# Output for items;
{'files': 0, 'data': [{'name': 'RFC', 'value': 'XXXXXXX', 'attId': '01'}, {'name': 'NOMBRE', 'value': 'JOSE', 'attId': '02'}, {'name': 'APELLIDO PATERNO', 'value': 'MONTIEL', 'attId': '03'}, {'name': 'APELLIDO MATERNO', 'value': 'MENDOZA', 'attId': '04'}, {'name': 'FECHA NACIMIENTO', 'value': '1989-02-04', 'attId': '05'}], 'dirId': 1, 'docId': 4, 'structure': {'name': 'personales', 'folioId': 22}}
{'files': 0, 'data': [{'name': 'CALLE', 'value': 'AMOR', 'attId': '06'}, {'name': 'No. EXTERIOR', 'value': '4', 'attId': '07'}, {'name': 'No. INTERIOR', 'value': '2', 'attId': '08'}, {'name': 'C.P.', 'value': '55060', 'attId': '09'}, {'name': 'ENTIDAD', 'value': 'ESTADO DE MEXICO', 'attId': '10'}, {'name': 'MUNICIPIO', 'value': 'ECATEPEC', 'attId': '11'}, {'name': 'COLONIA', 'value': 'INDUSTRIAL', 'attId': '12'}], 'dirId': 1, 'docId': 4, 'structure': {'name': 'direccion', 'folioId': 22}}

Sort list of nested dictionaries by multiple attributes

i have my sample data as
b = [{"id": 1, "name": {"d_name": "miranda", "ingredient": "orange"}, "score": 1.123},
{"id": 20, "name": {"d_name": "limca", "ingredient": "lime"}, "score": 4.231},
{"id": 3, "name": {"d_name": "coke", "ingredient": "water"}, "score": 4.231},
{"id": 2, "name": {"d_name": "fanta", "ingredient": "water"}, "score": 4.231},
{"id": 3, "name": {"d_name": "dew", "ingredient": "water & sugar"}, "score": 2.231}]
i need to sort such that score ASC, name DESC, id ASC (by relational db notation).
So far, i have implemented
def sort_func(e):
return (e['score'], e['name']['d_name'], e['id'])
a = b.sort(key=sort_func, reverse=False)
This works for score ASC, name ASC, id ASC.
but for score ASC, name DESC, id ASC if i try to sort by name DESC it throws error. because of unary - error in -e['name']['d_name'].
How can i approach this problem, from here ? Thanks,
Edit 1:
i need to make this dynamic such that there can be case such as e['name'['d_name'] ASC, e['name']['ingredient'] DESC. How can i handle this type of dynamic behaviour ?
You can sort by -score, name, -id with reverse=True:
from pprint import pprint
b = [
{
"id": 1,
"name": {"d_name": "miranda", "ingredient": "orange"},
"score": 1.123,
},
{
"id": 20,
"name": {"d_name": "limca", "ingredient": "lime"},
"score": 4.231,
},
{
"id": 3,
"name": {"d_name": "coke", "ingredient": "water"},
"score": 4.231,
},
{
"id": 2,
"name": {"d_name": "fanta", "ingredient": "water"},
"score": 4.231,
},
{
"id": 3,
"name": {"d_name": "dew", "ingredient": "water & sugar"},
"score": 2.231,
},
]
pprint(
sorted(
b,
key=lambda k: (-k["score"], k["name"]["d_name"], -k["id"]),
reverse=True,
)
)
Prints:
[{'id': 1,
'name': {'d_name': 'miranda', 'ingredient': 'orange'},
'score': 1.123},
{'id': 3,
'name': {'d_name': 'dew', 'ingredient': 'water & sugar'},
'score': 2.231},
{'id': 20, 'name': {'d_name': 'limca', 'ingredient': 'lime'}, 'score': 4.231},
{'id': 2, 'name': {'d_name': 'fanta', 'ingredient': 'water'}, 'score': 4.231},
{'id': 3, 'name': {'d_name': 'coke', 'ingredient': 'water'}, 'score': 4.231}]

Python, reorganize array of dicts

To be honest, it's too easy for me to make in JS or Perl, but i've completely stuck with that in Python because of coplexed tools for dealing with dicts/lists. So, what i need:
i have an array of dicts:
[
{"id": 1, "name": "Res1", "type": "resource", "k_name": "Ind1_1", "k_id": 4},
{"id": 1, "name": "Res1", "type": "resource", "k_name": "Ind1_2", "k_id": 5},
{"id": 1, "name": "Res1", "type": "resource", "k_name": "Ind1_3", "k_id": 6},
{"id": 2, "name": "Res2", "type": "service", "k_name": "Ind2_1", "k_id": 7},
{"id": 2, "name": "Res2", "type": "service", "k_name": "Ind2_2", "k_id": 8},
{"id": 2, "name": "Res2", "type": "service", "k_name": "Ind2_3", "k_id": 9},
{"id": 2, "name": "Res2", "type": "service", "k_name": "Ind2_4", "k_id": 10},
{"id": 3, "name": "Res3", "type": "service", "k_name": "Ind3_1", "k_id": 11},
{"id": 3, "name": "Res3", "type": "service", "k_name": "Ind3_2", "k_id": 12},
{"id": 3, "name": "Res3", "type": "service", "k_name": "Ind3_3", "k_id": 13},
{"id": 3, "name": "Res3", "type": "service", "k_name": "Ind3_4", "k_id": 14}
]
and i need to make that:
[
{
"id": 1,
"name": "Res1",
"type": "resource",
"indicators": [
{"name": "Ind1_1","id": 4},
{"name": "Ind1_2","id": 5},
{"name": "Ind1_3","id": 6}
]
},
{
"id": 2,
"name": "Res2",
"type": "service",
"indicators": [
{"name": "Ind2_1","id": 7},
{"name": "Ind2_2","id": 8},
{"name": "Ind2_3","id": 9},
{"name": "Ind2_4","id": 10}
]
},
{
"id": 3,
"name": "Res3",
"type": "service",
"indicators": [
{"name": "Ind3_1","id": 11},
{"name": "Ind3_2","id": 12},
{"name": "Ind3_3","id": 13},
{"name": "Ind3_4","id": 14}
]
}
]
Can you help me with that?
itertools to the rescue:
import itertools
# Assuming your original list is `l`
# if it does not come in order, you need to do this line first, and will probably be less efficient.
l = sorted(l, key=lambda x:(x["id"], x["name"], x["type"]))
d = []
for k, g in itertools.groupby(l, lambda x: (x["id"], x["name"], x["type"])):
d.append({i:v for i, v in zip(["id", "name", "type"], k)})
d[-1]["indicator"] = [{y.split('_')[1]:e[y] for y in ["k_id", "k_name"]} for e in list(g)]
d becomes:
[{'id': 1,
'indicator': [{'id': 4, 'name': 'Ind1_1'},
{'id': 5, 'name': 'Ind1_2'},
{'id': 6, 'name': 'Ind1_3'}],
'name': 'Res1',
'type': 'resource'},
{'id': 2,
'indicator': [{'id': 7, 'name': 'Ind2_1'},
{'id': 8, 'name': 'Ind2_2'},
{'id': 9, 'name': 'Ind2_3'},
{'id': 10, 'name': 'Ind2_4'}],
'name': 'Res2',
'type': 'service'},
{'id': 3,
'indicator': [{'id': 11, 'name': 'Ind3_1'},
{'id': 12, 'name': 'Ind3_2'},
{'id': 13, 'name': 'Ind3_3'},
{'id': 14, 'name': 'Ind3_4'}],
'name': 'Res3',
'type': 'service'}]
You can use a mapping dict to map ids to corresponding sub-lists, so that as you iterate through the list (named l in this example), you can append a new entry to the output list if the id is not found in the mapping, or append the entry to the existing sub-list if id is found in the mapping:
mapping = {}
output = []
for d in l:
i = {'name': d.pop('k_name'), 'id': d.pop('k_id')}
if d['id'] in mapping:
mapping[d['id']].append(i)
else:
output.append({**d, 'indicators': [i]})
mapping[d['id']] = output[-1]['indicators']
output becomes:
[{'id': 1, 'name': 'Res1', 'type': 'resource', 'indicators': [{'name': 'Ind1_1', 'id': 4}, {'name': 'Ind1_2', 'id': 5}, {'name': 'Ind1_3', 'id': 6}]}, {'id': 2, 'name': 'Res2', 'type': 'service', 'indicators': [{'name': 'Ind2_1', 'id': 7}, {'name': 'Ind2_2', 'id': 8}, {'name': 'Ind2_3', 'id': 9}, {'name': 'Ind2_4', 'id': 10}]}, {'id': 3, 'name': 'Res3', 'type': 'service', 'indicators': [{'name': 'Ind3_1', 'id': 11}, {'name': 'Ind3_2', 'id': 12}, {'name': 'Ind3_3', 'id': 13}, {'name': 'Ind3_4', 'id': 14}]}]

build Hierarchical comment tree?

I am trying to implement threaded comments in my django project which I want to look like this:
data
comment_list = [
{'id': 1, 'content': '...', 'pid': None, 'children_comments': []},
{'id': 2, 'content': '...', 'pid': None, 'children_comments': []},
{'id': 3, 'content': '...', 'pid': 1, 'children_comments': []},
{'id': 4, 'content': '...', 'pid': 3, 'children_comments': []},
{'id': 5, 'content': '...', 'pid': 4, 'children_comments': []},
{'id': 6, 'content': '...', 'pid': 2, 'children_comments': []},
{'id': 7, 'content': '...', 'pid': None, 'children_comments': []},
{'id': 8, 'content': '...', 'pid': 7, 'children_comments': []},
{'id': 9, 'content': '...', 'pid': None, 'children_comments': []},
{'id': 10, 'content': '...', 'pid': 9, 'children_comments': []},
]
for example
1
3
4
5
2
6
7
8
9
10
my code:
new = []
for comment in comment_list:
if comment['pid'] != None:
for i in comment_list:
if i['id'] == comment['pid']:
i['children_comments'].append(comment)
else:
new.append(comment)
i think that is not good
final in django jinjia how to show??
Sure.
The algorithm is as follows:
iterate over the flat list of comments, gathering up comments by their parentage into a comments_by_parent mapping
iterate over the list again, assigning children_comments from the comments_by_parent mapping
get the root-level comments
from collections import defaultdict
comment_list = [
{'id': 1, 'content': '...', 'pid': None},
{'id': 2, 'content': '...', 'pid': None},
{'id': 3, 'content': '...', 'pid': 1},
{'id': 4, 'content': '...', 'pid': 3},
{'id': 5, 'content': '...', 'pid': 4},
{'id': 6, 'content': '...', 'pid': 2},
{'id': 7, 'content': '...', 'pid': None},
{'id': 8, 'content': '...', 'pid': 7},
{'id': 9, 'content': '...', 'pid': None},
{'id': 10, 'content': '...', 'pid': 9},
]
comments_by_parent = defaultdict(list)
for comment in comment_list:
comments_by_parent[comment['pid']].append(comment)
for comment in comment_list:
comment['children_comments'] = comments_by_parent[comment['id']]
root_comments = comments_by_parent[None]
root_comments will end up looking like this (JSON output for clarity).
[
{
"id": 1,
"content": "...",
"pid": null,
"children_comments": [
{
"id": 3,
"content": "...",
"pid": 1,
"children_comments": [
{
"id": 4,
"content": "...",
"pid": 3,
"children_comments": [
{
"id": 5,
"content": "...",
"pid": 4,
"children_comments": []
}
]
}
]
}
]
},
{
"id": 2,
"content": "...",
"pid": null,
"children_comments": [
{
"id": 6,
"content": "...",
"pid": 2,
"children_comments": []
}
]
},
{
"id": 7,
"content": "...",
"pid": null,
"children_comments": [
{
"id": 8,
"content": "...",
"pid": 7,
"children_comments": []
}
]
},
{
"id": 9,
"content": "...",
"pid": null,
"children_comments": [
{
"id": 10,
"content": "...",
"pid": 9,
"children_comments": []
}
]
}
]
You can then output this in Jinja using a recursive for loop:
<ul>
{%- for comment in root_comments recursive %}
<li>
{{ comment.content }}
{%- if comment.children_comments -%}
<ul>{{ loop(comment.children_comments) }}</ul>
{%- endif %}
</li>
{%- endfor %}
</ul>

Categories

Resources