Howdy folks, Currently I build my chat menus by hand/spreadsheet to aid in quickly navigating from scene to scene. What I would really like, however, is a chat menu that is generated from my Journal structure. So let's say I currently have: Scene Manager/ Scene Manager/Forest/ Scene Manager/Forest/Faerie Woods/ Scene Manager/Forest/Faerie Woods/Faerie Ring Glade Scene Manager/Ocean/ Scene Manager/Ocean/Windhome Scene Manager/Home Base I can parse Campaign._journalFolder and expand the contents to get an array of objects. [{"n":"PCs","i":[{"name":"A Player Character","bio":"","gmnotes":"","_defaulttoken":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-NMZoQR1Bz1YLiuXhp9Q","_type":"character","avatar":""}],"id":"-NMZoPWAiPBNcx10aJXB"},{"n":"Scene Manager","i":[{"n":"Forest","i":[{"n":"Faerie Woods","i":[{"name":"Faerie Ring Glade","bio":"","gmnotes":"","_defaulttoken":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-NMVuobOxHD8qaGdXy0D","_type":"character","avatar":""}],"id":"-NMVunKEDbmeUNtTI1eN"}],"id":"-NMVukiUC7fZ-RYz3oc9"},{"n":"Ocean","i":[{"name":"Windhome","bio":"","gmnotes":"","_defaulttoken":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-NMVv6EixllSd2uWjYJB","_type":"character","avatar":""}],"id":"-NMVv34MwHbpuU0wGSZe"},{"name":"Home Base","bio":"","gmnotes":"","_defaulttoken":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-NMVuybA_n0d53UfOU3L","_type":"character","avatar":""}],"id":"-NMVuhHo71u6Dsg0vTSU"}] I want to recursively loop through that in order to generate a hierarchical series of chat menus that look something like this (mock-ups): The Ask What I don't want is someone to write this for me. What I do want is help thinking through the approach, because I think this will be a fun JavaScript tutorial project for myself. I would love some assistance with the pseudocode that'll get me to where I want from here. // Thanks to Lithl for sharing this function on the Roll20 forums // <a href="https://app.roll20.net/forum/post/4638074/read-journal-folder-contents" rel="nofollow">https://app.roll20.net/forum/post/4638074/read-journal-folder-contents</a> function getFolderObjects ( objs ) { // _.map takes a collection and uses some function to transform each element into something else return _ . map ( objs , function ( o ) { // if the element isn't a string, it should be an object; one of its properties should be i, which should be an array if ( _ . isArray ( o . i )) { // the i property is the contents of the folder, so recursively call this function to get the objects in the folder o . i = getFolderObjects ( o . i ); // return the folder return o ; } // check if the current element of the collection is a string (an id for a character or handout) if ( _ . isString ( o )) { // return the handout, or the character if it's not a handout's id return getObj ( 'handout' , o ) || getObj ( 'character' , o ); } }); } on ( "ready" , function () { // Gets all the objects in the campaign and parses the JSON const journalEntries = getFolderObjects ( JSON . parse ( Campaign (). get ( "_journalfolder" ))); /* In the dev environment, this returns: [{"n":"PCs","i":[{"name":"A Player Character","bio":"","gmnotes":"","_defaulttoken":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-NMZoQR1Bz1YLiuXhp9Q","_type":"character","avatar":""}],"id":"-NMZoPWAiPBNcx10aJXB"},{"n":"Scene Manager","i":[{"n":"Forest","i":[{"n":"Faerie Woods","i":[{"name":"Faerie Ring Glade","bio":"","gmnotes":"","_defaulttoken":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-NMVuobOxHD8qaGdXy0D","_type":"character","avatar":""}],"id":"-NMVunKEDbmeUNtTI1eN"}],"id":"-NMVukiUC7fZ-RYz3oc9"},{"n":"Ocean","i":[{"name":"Windhome","bio":"","gmnotes":"","_defaulttoken":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-NMVv6EixllSd2uWjYJB","_type":"character","avatar":""}],"id":"-NMVv34MwHbpuU0wGSZe"},{"name":"Home Base","bio":"","gmnotes":"","_defaulttoken":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-NMVuybA_n0d53UfOU3L","_type":"character","avatar":""}],"id":"-NMVuhHo71u6Dsg0vTSU"}] */ // Checks that the Scene Manager folder exists already somewhere in the root directory let readySceneManager = false ; for ( let x in journalEntries ) { folderState = Object . values ( journalEntries [ x ]). includes ( "Scene Manager" ); if ( folderState === true ) { // Flag the Scene Manager Macro as ready to be built readySceneManager = x ; // debugging log log ( "Scene Manager folder exists" ); } } if ( readySceneManager === false ) { // Roll20 does not allow the creation of folders via API // Next best thing: Send a message to chat that the folder is missing sendChat ( "Scene Manager" , "'Scene Manager' folder is missing at the root level. Please create this folder and type '!scmgr --rebuild' in chat to proceed." ) // debugging log log ( "Scene Manager folder does not exist" ); } // debugging log log ( journalEntries ); });