I am trying to make a new dictionary that is made from an existing dictionary that it has been creating from a function which tries to merge two dictionaries that derive from json files from backup firefox bookmarks.
1st json bookmark dictionary:
json1={'guid': 'root________', 'title': '', 'index': 0, 'dateAdded': 1688927106926000, 'lastModified': 1697130233008000, 'id': 1, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'placesRoot', 'children': [{'guid': 'menu________', 'title': 'menu', 'index': 0, 'dateAdded': 1688927106926000, 'lastModified': 1697130233008000, 'id': 2, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'bookmarksMenuFolder', 'children': [{'guid': '9GEAbdFPVBqv', 'title': 'Getting started - mypy 1.5.1 documentation', 'index': 0, 'dateAdded': 1696274666207000, 'lastModified': 1696274666207000, 'id': 16, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://mypy.readthedocs.io/en/stable/getting_started.html'}, {'guid': 'PDKXoMPpSKZ9', 'title': 'testFolder2', 'index': 1, 'dateAdded': 1697130042452000, 'lastModified': 1697130183178000, 'id': 18, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'jbP4ff424REs', 'title': 'Secure Coding with Python', 'index': 0, 'dateAdded': 1697130058445000, 'lastModified': 1697130058445000, 'id': 19, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://devopedia.org/secure-coding-with-python'}, {'guid': 'bZSAKQe67MEP', 'title': 'testSubFolder', 'index': 1, 'dateAdded': 1697130074677000, 'lastModified': 1697130183178000, 'id': 20, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': '0U5O4Rw6M3M5', 'title': 'Typer', 'index': 0, 'dateAdded': 1697130183178000, 'lastModified': 1697130183178000, 'id': 21, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://typer.tiangolo.com/'}]}]}, {'guid': '-j27AP1Cwt0O', 'title': 'testFolder1', 'index': 2, 'dateAdded': 1697130021758000, 'lastModified': 1697130233008000, 'id': 17, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'Wb3-R2DDT8Ip', 'title': 'Welcome to Click — Click Documentation (8.1.x)', 'index': 0, 'dateAdded': 1697130230240000, 'lastModified': 1697130230240000, 'id': 22, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://click.palletsprojects.com/en/8.1.x/'}]}, {'guid': 'VDTmkniLNlvN', 'title': 'Mozilla Firefox', 'index': 3, 'dateAdded': 1688927107386000, 'lastModified': 1697129949344000, 'id': 7, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'vUwrKuzYfywC', 'title': 'Get Help', 'index': 0, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 8, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://support.mozilla.org/products/firefox', 'type': 'text/x-moz-place', 'uri': 'https://support.mozilla.org/products/firefox'}, {'guid': 'mKpEl6U5Pppr', 'title': 'Customize Firefox', 'index': 1, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 9, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://support.mozilla.org/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize', 'type': 'text/x-moz-place', 'uri': 'https://support.mozilla.org/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize'}, {'guid': 'Rw167-bbT1fR', 'title': 'Get Involved', 'index': 2, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 10, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/contribute/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/contribute/'}, {'guid': 'stHPEtkREVvD', 'title': 'About Us', 'index': 3, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 11, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/about/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/about/'}]}]}, {'guid': 'toolbar_____', 'title': 'toolbar', 'index': 1, 'dateAdded': 1688927106926000, 'lastModified': 1696274298931000, 'id': 3, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'toolbarFolder', 'children': [{'guid': '690YbVlf5eS_', 'title': 'Getting Started', 'index': 0, 'dateAdded': 1688927107484000, 'lastModified': 1688927107484000, 'id': 12, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/firefox/central/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/firefox/central/'}, {'guid': 'YJ_gjoZ6Wwj1', 'title': 'json — JSON encoder and decoder — Python 3.11.5 documentation', 'index': 1, 'dateAdded': 1696274298931000, 'lastModified': 1696274298931000, 'id': 13, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.python.org/3/library/json.html'}]}, {'guid': 'unfiled_____', 'title': 'unfiled', 'index': 3, 'dateAdded': 1688927106926000, 'lastModified': 1688927107332000, 'id': 5, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'unfiledBookmarksFolder'}, {'guid': 'mobile______', 'title': 'mobile', 'index': 4, 'dateAdded': 1688927106942000, 'lastModified': 1688927107332000, 'id': 6, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'mobileFolder'}]}
2nd json bookmark dictionary:
json2={'guid': 'root________', 'title': '', 'index': 0, 'dateAdded': 1688927106926000, 'lastModified': 1697131416648000, 'id': 1, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'placesRoot', 'children': [{'guid': 'menu________', 'title': 'menu', 'index': 0, 'dateAdded': 1688927106926000, 'lastModified': 1697131416648000, 'id': 2, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'bookmarksMenuFolder', 'children': [{'guid': '9GEAbdFPVBqv', 'title': 'Getting started - mypy 1.5.1 documentation', 'index': 0, 'dateAdded': 1696274666207000, 'lastModified': 1696274666207000, 'id': 16, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://mypy.readthedocs.io/en/stable/getting_started.html'}, {'guid': 'FNxknWay_xr8', 'title': "Command-line Applications — The Hitchhiker's Guide to Python", 'index': 1, 'dateAdded': 1697130502023000, 'lastModified': 1697130502023000, 'id': 23, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.python-guide.org/scenarios/cli/'}, {'guid': 'PDKXoMPpSKZ9', 'title': 'testFolder2', 'index': 2, 'dateAdded': 1697130042452000, 'lastModified': 1697131416648000, 'id': 18, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'bZSAKQe67MEP', 'title': 'testSubFolder', 'index': 0, 'dateAdded': 1697130074677000, 'lastModified': 1697131416648000, 'id': 20, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'm6MBObvLXgt6', 'title': 'The Python Fire Guide - Python Fire', 'index': 0, 'dateAdded': 1697130663668000, 'lastModified': 1697130663668000, 'id': 28, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://google.github.io/python-fire/guide/'}, {'guid': 'fl2vHRLT-RJY', 'title': 'Typer', 'index': 1, 'dateAdded': 1697131416648000, 'lastModified': 1697131416648000, 'id': 30, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://typer.tiangolo.com/'}]}, {'guid': 'Z2khP-DX2nJU', 'title': 'testsubF2', 'index': 1, 'dateAdded': 1697130537074000, 'lastModified': 1697130642695000, 'id': 26, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'ZHUGVs2ZYUiA', 'title': 'argparse — Parser for command-line options, arguments and sub-commands — Python 3.12.0 documentation', 'index': 0, 'dateAdded': 1697130642695000, 'lastModified': 1697130642695000, 'id': 27, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.python.org/3/library/argparse.html'}]}, {'guid': '3RP_KOI4Pq0q', 'title': 'plac · PyPI', 'index': 2, 'dateAdded': 1697130513781000, 'lastModified': 1697130513781000, 'id': 24, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://pypi.org/project/plac/'}]}, {'guid': '-j27AP1Cwt0O', 'title': 'testFolder1', 'index': 3, 'dateAdded': 1697130021758000, 'lastModified': 1697130520562000, 'id': 17, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'Wb3-R2DDT8Ip', 'title': 'Welcome to Click — Click Documentation (8.1.x)', 'index': 0, 'dateAdded': 1697130230240000, 'lastModified': 1697130230240000, 'id': 22, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://click.palletsprojects.com/en/8.1.x/'}, {'guid': 'zuT4_jp_Rj5l', 'title': 'cliff – Command Line Interface Formulation Framework — cliff 4.3.1.dev12 documentation', 'index': 1, 'dateAdded': 1697130520562000, 'lastModified': 1697130520562000, 'id': 25, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.openstack.org/cliff/latest/'}]}, {'guid': 'LjrKYDavnU7w', 'title': 'Generating Command-Line Interfaces (CLI) with Fire in Python', 'index': 4, 'dateAdded': 1697130696550000, 'lastModified': 1697130696550000, 'id': 29, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://stackabuse.com/generating-command-line-interfaces-cli-with-fire-in-python/'}, {'guid': 'VDTmkniLNlvN', 'title': 'Mozilla Firefox', 'index': 5, 'dateAdded': 1688927107386000, 'lastModified': 1697129949344000, 'id': 7, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'vUwrKuzYfywC', 'title': 'Get Help', 'index': 0, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 8, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://support.mozilla.org/products/firefox', 'type': 'text/x-moz-place', 'uri': 'https://support.mozilla.org/products/firefox'}, {'guid': 'mKpEl6U5Pppr', 'title': 'Customize Firefox', 'index': 1, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 9, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://support.mozilla.org/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize', 'type': 'text/x-moz-place', 'uri': 'https://support.mozilla.org/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize'}, {'guid': 'Rw167-bbT1fR', 'title': 'Get Involved', 'index': 2, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 10, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/contribute/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/contribute/'}, {'guid': 'stHPEtkREVvD', 'title': 'About Us', 'index': 3, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 11, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/about/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/about/'}]}]}, {'guid': 'toolbar_____', 'title': 'toolbar', 'index': 1, 'dateAdded': 1688927106926000, 'lastModified': 1696274298931000, 'id': 3, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'toolbarFolder', 'children': [{'guid': '690YbVlf5eS_', 'title': 'Getting Started', 'index': 0, 'dateAdded': 1688927107484000, 'lastModified': 1688927107484000, 'id': 12, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/firefox/central/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/firefox/central/'}, {'guid': 'YJ_gjoZ6Wwj1', 'title': 'json — JSON encoder and decoder — Python 3.11.5 documentation', 'index': 1, 'dateAdded': 1696274298931000, 'lastModified': 1696274298931000, 'id': 13, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.python.org/3/library/json.html'}]}, {'guid': 'unfiled_____', 'title': 'unfiled', 'index': 3, 'dateAdded': 1688927106926000, 'lastModified': 1688927107332000, 'id': 5, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'unfiledBookmarksFolder'}, {'guid': 'mobile______', 'title': 'mobile', 'index': 4, 'dateAdded': 1688927106942000, 'lastModified': 1688927107332000, 'id': 6, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'mobileFolder'}]}
My idea was to simplify the merging of these two dictionaries by doing:
def has_url(diction):
if "uri" in diction:
return True
else:
return False
def merge_dicts(dictmain, dict2):
return {**dict2, **dictmain}
def get_bm_path(bookmarks):
urls = {}
def bm_path(x, name=""):
if type(x) is dict:
name = name + x.get("guid") + "/"
if has_url(x):
urls[name] = [f"uri_{x.get('guid')}", dict((k, x[k]) for k in x.keys() if k not in "children")]
else:
urls[name] = [f"folder_{x.get('guid')}", dict((k, x[k]) for k in x.keys() if k not in "children")]
bm_path(x=x.get("children"), name=name)
elif type(x) is list:
for i, a in enumerate(x):
bm_path(a, name=name)
bm_path(bookmarks)
return urls
Then by running:
merged_dict = merge_dicts(get_bm_path(json1), get_bm_path(json2))
I get a merged dictionary with keys that resemble folders/files structure so as not to lose the track of the parent/child structure of bookmarks while merging.
And then I need to make the function to derive from that merged dictionary the actual json structure that firefox uses.
The minimal (maybe) reproducible example:
main dictionary:
{'main_folder/': {'id': 'main_folder', 'ad': 'what'}, 'main_folder/subfolder1/': {'id': 'subfolder1', 'ad': 'what'}, 'main_folder/subfolder1/9GEAbdFPVBqv/': {'id': '9GEAbdFPVBqv', 'ad': 'what1'},
'main_folder/subfolder1/eaXY8H5Y1cJ_/': {'id': 'eaXY8H5Y1cJ_', 'ad': 'what2'},
'main_folder/subfolder1/eaXY8H5Y1cJ_/9p2UFp7-qcEt/': {'id': '9p2UFp7-qcEt', 'ad': 'what3'},
'main_folder/subfolder1/fijaCypbmbU1/': {'id': 'fijaCypbmbU1', 'ad': 'what4'},
'main_folder/subfolder2/': {'id': 'subfolder2', 'ad': 'what7'}}
The resulting dictionary should be something like that:
{'id': 'main_folder', 'ad': 'what',
'children':[
{'id': 'subfolder1', 'ad': 'what',
'children':[
{'id': '9GEAbdFPVBqv', 'ad': 'what1'},
{'id': 'eaXY8H5Y1cJ_', 'ad': 'what2',
'children': [{'id': '9p2UFp7-qcEt', 'ad': 'what3'}]},
{'id': 'fijaCypbmbU1', 'ad': 'what4'}
]
},
{'id': 'subfolder2', 'ad': 'what7'}
]
}
The resulting dictionary makes the tree like structure for each element (sub-folder) in a parent/child dictionary/list. I can't find a way to reduce each "layer" of files to subfolders recursively.
To be able to merge these easily, one realization needs to be had ~ every dict
in the entire structure is a dict
that may have a children
key. Since that key is a "maybe" we can derive that at the very least, every dict
is just a dict
, and we should treat them identically.
A very good way to do this is through the use of recursion. We can input the entire structure and have it feed child dictionaries back into itself so, everything goes through the exact same process.
The code works as such:
The current data
dict to be merged is passed simultaneously with the targ
dict to be merged to. If the targ
dict is empty, it is immediately updated with the data
dict contents. Then all of the children
of the data
dict are iterated. On each iteration a check is made to find a child on targ
with a matching guid
. Matches are returned as the index of the match within the children
list. If no match is found the index is defaulted to -1
. This is because, if no match is found we are going to append an empty dict to the end of the list. Finally the current child of data['children']
and the dict at the index of targ['children']
are passed back into the function and the process starts over.
It's just a bunch of "do my children guids match your children guids?" and if not, make as many new dictionaries as necessary to accommodate the new children. If there is a matching child send the matches through to see if their children match. On and on until there is no more data.
Triggering the update
line is the equivalent of saying. "I didn't have this child but, now I do.", with the only exception being the very first call of r_merge
. In that case it's like saying "I didn't have anything, and now I am identical to all of this first bookmark object". That also means that after the very first update
nothing really happens until you get to the 2nd bookmark object. It just runs through children lists that perfectly match.
#merge data to targ by eid recursing on r_key
def r_merge(data:dict, targ:dict, eid:str, r_key:str) -> None:
#guard clause
if not data.get(eid): raise ValueError
#update if empty
if not targ: targ.update(data)
#process children
for entry in data.get(r_key, []):
#find match index else default to -1
i = next((i for i, ent in enumerate(targ[r_key]) if ent[eid] == entry[eid]), -1)
#no match, prime with empty dict
if i<0: targ[r_key].append(dict())
#recursive merge
r_merge(entry, targ[r_key][i], eid, r_key)
import json
json1={'guid': 'root________', 'title': '', 'index': 0, 'dateAdded': 1688927106926000, 'lastModified': 1697130233008000, 'id': 1, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'placesRoot', 'children': [{'guid': 'menu________', 'title': 'menu', 'index': 0, 'dateAdded': 1688927106926000, 'lastModified': 1697130233008000, 'id': 2, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'bookmarksMenuFolder', 'children': [{'guid': '9GEAbdFPVBqv', 'title': 'Getting started - mypy 1.5.1 documentation', 'index': 0, 'dateAdded': 1696274666207000, 'lastModified': 1696274666207000, 'id': 16, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://mypy.readthedocs.io/en/stable/getting_started.html'}, {'guid': 'PDKXoMPpSKZ9', 'title': 'testFolder2', 'index': 1, 'dateAdded': 1697130042452000, 'lastModified': 1697130183178000, 'id': 18, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'jbP4ff424REs', 'title': 'Secure Coding with Python', 'index': 0, 'dateAdded': 1697130058445000, 'lastModified': 1697130058445000, 'id': 19, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://devopedia.org/secure-coding-with-python'}, {'guid': 'bZSAKQe67MEP', 'title': 'testSubFolder', 'index': 1, 'dateAdded': 1697130074677000, 'lastModified': 1697130183178000, 'id': 20, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': '0U5O4Rw6M3M5', 'title': 'Typer', 'index': 0, 'dateAdded': 1697130183178000, 'lastModified': 1697130183178000, 'id': 21, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://typer.tiangolo.com/'}]}]}, {'guid': '-j27AP1Cwt0O', 'title': 'testFolder1', 'index': 2, 'dateAdded': 1697130021758000, 'lastModified': 1697130233008000, 'id': 17, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'Wb3-R2DDT8Ip', 'title': 'Welcome to Click — Click Documentation (8.1.x)', 'index': 0, 'dateAdded': 1697130230240000, 'lastModified': 1697130230240000, 'id': 22, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://click.palletsprojects.com/en/8.1.x/'}]}, {'guid': 'VDTmkniLNlvN', 'title': 'Mozilla Firefox', 'index': 3, 'dateAdded': 1688927107386000, 'lastModified': 1697129949344000, 'id': 7, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'vUwrKuzYfywC', 'title': 'Get Help', 'index': 0, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 8, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://support.mozilla.org/products/firefox', 'type': 'text/x-moz-place', 'uri': 'https://support.mozilla.org/products/firefox'}, {'guid': 'mKpEl6U5Pppr', 'title': 'Customize Firefox', 'index': 1, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 9, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://support.mozilla.org/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize', 'type': 'text/x-moz-place', 'uri': 'https://support.mozilla.org/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize'}, {'guid': 'Rw167-bbT1fR', 'title': 'Get Involved', 'index': 2, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 10, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/contribute/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/contribute/'}, {'guid': 'stHPEtkREVvD', 'title': 'About Us', 'index': 3, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 11, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/about/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/about/'}]}]}, {'guid': 'toolbar_____', 'title': 'toolbar', 'index': 1, 'dateAdded': 1688927106926000, 'lastModified': 1696274298931000, 'id': 3, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'toolbarFolder', 'children': [{'guid': '690YbVlf5eS_', 'title': 'Getting Started', 'index': 0, 'dateAdded': 1688927107484000, 'lastModified': 1688927107484000, 'id': 12, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/firefox/central/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/firefox/central/'}, {'guid': 'YJ_gjoZ6Wwj1', 'title': 'json — JSON encoder and decoder — Python 3.11.5 documentation', 'index': 1, 'dateAdded': 1696274298931000, 'lastModified': 1696274298931000, 'id': 13, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.python.org/3/library/json.html'}]}, {'guid': 'unfiled_____', 'title': 'unfiled', 'index': 3, 'dateAdded': 1688927106926000, 'lastModified': 1688927107332000, 'id': 5, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'unfiledBookmarksFolder'}, {'guid': 'mobile______', 'title': 'mobile', 'index': 4, 'dateAdded': 1688927106942000, 'lastModified': 1688927107332000, 'id': 6, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'mobileFolder'}]}
json2={'guid': 'root________', 'title': '', 'index': 0, 'dateAdded': 1688927106926000, 'lastModified': 1697131416648000, 'id': 1, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'placesRoot', 'children': [{'guid': 'menu________', 'title': 'menu', 'index': 0, 'dateAdded': 1688927106926000, 'lastModified': 1697131416648000, 'id': 2, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'bookmarksMenuFolder', 'children': [{'guid': '9GEAbdFPVBqv', 'title': 'Getting started - mypy 1.5.1 documentation', 'index': 0, 'dateAdded': 1696274666207000, 'lastModified': 1696274666207000, 'id': 16, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://mypy.readthedocs.io/en/stable/getting_started.html'}, {'guid': 'FNxknWay_xr8', 'title': "Command-line Applications — The Hitchhiker's Guide to Python", 'index': 1, 'dateAdded': 1697130502023000, 'lastModified': 1697130502023000, 'id': 23, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.python-guide.org/scenarios/cli/'}, {'guid': 'PDKXoMPpSKZ9', 'title': 'testFolder2', 'index': 2, 'dateAdded': 1697130042452000, 'lastModified': 1697131416648000, 'id': 18, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'bZSAKQe67MEP', 'title': 'testSubFolder', 'index': 0, 'dateAdded': 1697130074677000, 'lastModified': 1697131416648000, 'id': 20, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'm6MBObvLXgt6', 'title': 'The Python Fire Guide - Python Fire', 'index': 0, 'dateAdded': 1697130663668000, 'lastModified': 1697130663668000, 'id': 28, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://google.github.io/python-fire/guide/'}, {'guid': 'fl2vHRLT-RJY', 'title': 'Typer', 'index': 1, 'dateAdded': 1697131416648000, 'lastModified': 1697131416648000, 'id': 30, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://typer.tiangolo.com/'}]}, {'guid': 'Z2khP-DX2nJU', 'title': 'testsubF2', 'index': 1, 'dateAdded': 1697130537074000, 'lastModified': 1697130642695000, 'id': 26, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'ZHUGVs2ZYUiA', 'title': 'argparse — Parser for command-line options, arguments and sub-commands — Python 3.12.0 documentation', 'index': 0, 'dateAdded': 1697130642695000, 'lastModified': 1697130642695000, 'id': 27, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.python.org/3/library/argparse.html'}]}, {'guid': '3RP_KOI4Pq0q', 'title': 'plac · PyPI', 'index': 2, 'dateAdded': 1697130513781000, 'lastModified': 1697130513781000, 'id': 24, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://pypi.org/project/plac/'}]}, {'guid': '-j27AP1Cwt0O', 'title': 'testFolder1', 'index': 3, 'dateAdded': 1697130021758000, 'lastModified': 1697130520562000, 'id': 17, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'Wb3-R2DDT8Ip', 'title': 'Welcome to Click — Click Documentation (8.1.x)', 'index': 0, 'dateAdded': 1697130230240000, 'lastModified': 1697130230240000, 'id': 22, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://click.palletsprojects.com/en/8.1.x/'}, {'guid': 'zuT4_jp_Rj5l', 'title': 'cliff – Command Line Interface Formulation Framework — cliff 4.3.1.dev12 documentation', 'index': 1, 'dateAdded': 1697130520562000, 'lastModified': 1697130520562000, 'id': 25, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.openstack.org/cliff/latest/'}]}, {'guid': 'LjrKYDavnU7w', 'title': 'Generating Command-Line Interfaces (CLI) with Fire in Python', 'index': 4, 'dateAdded': 1697130696550000, 'lastModified': 1697130696550000, 'id': 29, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://stackabuse.com/generating-command-line-interfaces-cli-with-fire-in-python/'}, {'guid': 'VDTmkniLNlvN', 'title': 'Mozilla Firefox', 'index': 5, 'dateAdded': 1688927107386000, 'lastModified': 1697129949344000, 'id': 7, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'children': [{'guid': 'vUwrKuzYfywC', 'title': 'Get Help', 'index': 0, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 8, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://support.mozilla.org/products/firefox', 'type': 'text/x-moz-place', 'uri': 'https://support.mozilla.org/products/firefox'}, {'guid': 'mKpEl6U5Pppr', 'title': 'Customize Firefox', 'index': 1, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 9, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://support.mozilla.org/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize', 'type': 'text/x-moz-place', 'uri': 'https://support.mozilla.org/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize'}, {'guid': 'Rw167-bbT1fR', 'title': 'Get Involved', 'index': 2, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 10, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/contribute/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/contribute/'}, {'guid': 'stHPEtkREVvD', 'title': 'About Us', 'index': 3, 'dateAdded': 1688927107386000, 'lastModified': 1688927107386000, 'id': 11, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/about/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/about/'}]}]}, {'guid': 'toolbar_____', 'title': 'toolbar', 'index': 1, 'dateAdded': 1688927106926000, 'lastModified': 1696274298931000, 'id': 3, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'toolbarFolder', 'children': [{'guid': '690YbVlf5eS_', 'title': 'Getting Started', 'index': 0, 'dateAdded': 1688927107484000, 'lastModified': 1688927107484000, 'id': 12, 'typeCode': 1, 'iconUri': 'fake-favicon-uri:https://www.mozilla.org/firefox/central/', 'type': 'text/x-moz-place', 'uri': 'https://www.mozilla.org/firefox/central/'}, {'guid': 'YJ_gjoZ6Wwj1', 'title': 'json — JSON encoder and decoder — Python 3.11.5 documentation', 'index': 1, 'dateAdded': 1696274298931000, 'lastModified': 1696274298931000, 'id': 13, 'typeCode': 1, 'type': 'text/x-moz-place', 'uri': 'https://docs.python.org/3/library/json.html'}]}, {'guid': 'unfiled_____', 'title': 'unfiled', 'index': 3, 'dateAdded': 1688927106926000, 'lastModified': 1688927107332000, 'id': 5, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'unfiledBookmarksFolder'}, {'guid': 'mobile______', 'title': 'mobile', 'index': 4, 'dateAdded': 1688927106942000, 'lastModified': 1688927107332000, 'id': 6, 'typeCode': 2, 'type': 'text/x-moz-place-container', 'root': 'mobileFolder'}]}
results = dict() #for storing results
datas = (json1, json2) #gather dicts for iteration
for data in datas:
r_merge(data, results, 'guid', 'children') #recursive merge
#print results
print(json.dumps(results, indent=4))