I am trying to reproduce a list with nested sublists so that I can recreate it in another application. Right now, I’m trying to copy The Hit List tasks from The Hit List to OmniOutliner, but I’ve run up against this problem with other applications as well, and what I’m really trying to figure out is how to do a general purpose recursion. So I’m envisioning pseudocode something like this…
property theList : {}
on copy_list(topLevelList)
set listChildren to every item in topLevelList
set theList to theList & {listChildren}
repeat with i from 1 to (count of listChildren)
my copy_list(item i of listChildren)
end repeat
return theList
end copy_list
…but not exactly this, because this doesn’t work.
I just know this can be done, but I can’t figure out how. Can anyone help me with some recursion basics?
Hi,
have to say, that i’m not sure if I got you right … but it’s for sure a recursion basic …
some code:
global theList
set theList to {"some Input here"}
set listofLists to {{"1", "2"}, "3", "4", {"5", "6"}}
my splitList(listofLists)
on splitList(aList)
repeat with i from 1 to count of aList
set theListItem to item i of aList
if class of theListItem is equal to list then
my splitList(theListItem)
else
set end of theList to theListItem
end if
end repeat
return theList
end splitList
--result: {"some Input here", "1", "2", "3", "4", "5", "6"}
Okay, so I’m trying to start with something like:
–File list ------------------------
1st level item 1
–2nd level item 1
–2nd level item 2
----3nd level item 1 (contained in 2nd level item 2)
–2nd level item 3
And get back something like…
{“1st level item 1”,¬
{¬
“2nd level item 1”, {}¬
“2nd level item 2”, {“3nd level item 1 (contained in 2nd level item 2)”, {}}, ¬
“2nd level item 3”, {}¬
}¬
}
This is my current non-functioning code:
property nameList : {"", {}}
tell application "Finder" to set folderRef to item 1 of (selection as list)
return my get_list_of_file_names_in_folder(folderRef)
on get_list_of_file_names_in_folder(folderRef)
tell application "Finder"
set nameHolder to {"", {}}
tell application "Finder" to set currRef to every item of folderRef
set item 1 of nameHolder to name of folderRef
repeat with i from 1 to (count of currRef)
tell application "Finder" to set item 2 of nameHolder to item 2 of nameHolder & {name of item i of currRef}
my get_list_of_file_names_in_folder(item i of currRef)
end repeat
set nameList to nameHolder & nameList
return nameList
end tell
end get_list_of_file_names_in_folder
I just cannot wrap my head around what variables should be set at what points and where I should call the recurring function. I can get a flat list of files pretty easily, but I’m trying to keep the structure intact so that I can recreate it.
Okay, so some progress and a new approach:
property nameList : {}
property itemNum : 1
property levelNum : 1
tell application "Finder" to set folderRef to item 1 of (selection as list) -- If you just use "return selection" with a single item selected, you get {folder "Tag Catalog" of folder " iLibrary" of folder " My Files" of folder "rloconne" of folder "Users" of startup disk of application "Finder"}, but you can't choose item 1 of this return value, and if you ask for "items of selection", you get an empty list, even if there are items in the folders selected. So, coerce the selection into a proper list, select the first (and only) item in it, and go on about your business.
set itemNum to 1 --Reset the itemNum, levelNum, and nameList each time the script is run
set levelNum to 1
set nameList to {}
return my get_list_of_file_names_in_folder(folderRef)
on get_list_of_file_names_in_folder(folderRef)
tell application "Finder"
set currRef to every item of folderRef
set the end of nameList to {itemData:name of folderRef, itemNum:itemNum, levelNum:levelNum}
repeat with i from 1 to (count of currRef)
if i is 1 then set levelNum to levelNum + 1
set itemNum to itemNum + 1
my get_list_of_file_names_in_folder(item i of currRef)
if i is (count of currRef) then set levelNum to levelNum - 1
end repeat
return nameList
end tell
end get_list_of_file_names_in_folder
(* For folder structure
L1
--L2-child of 'L1' (1)
----L3-child of 'L2-child of 'L1' (1)' (1)
-------L4-child of 'L3-child of 'L2-child of 'L1' (1)' (1)' (1)
--------L5-child of 'L4-child of 'L3-child of 'L2-child of 'L1' (1)' (1)' (1)' (1)
----L3-child of 'L2-child of 'L1' (1)' (2)
--L2-child of 'L1' (2)
-- Return value {{itemData:"L1", itemNum:1, levelNum:1}, {itemData:"L2-child of 'L1' (1)", itemNum:2, levelNum:2}, {itemData:"L3-child of 'L2-child of 'L1' (1)' (1)", itemNum:3, levelNum:3}, {itemData:"L4-child of 'L3-child of 'L2-child of 'L1' (1)' (1)' (1)", itemNum:4, levelNum:4}, {itemData:"L5-child of 'L4-child of 'L3-child of 'L2-child of 'L1' (1)' (1)' (1)' (1)", itemNum:5, levelNum:5}, {itemData:"L3-child of 'L2-child of 'L1' (1)' (2)", itemNum:6, levelNum:3}, {itemData:"L2-child of 'L1' (2)", itemNum:7, levelNum:2}}
*)
With this approach, I create a flat list of files, but each file record contains an “address” consisting its item number (its index in a flat list of all of the files) and its level number (how many levels “deep” it is), which makes for an easier to read result, while still making it possible to recreate the structured list.
Bonus code:
Make an OmniOutliner list using your addressed list:
on make_outline_from_addressed_list(addressedList)
-- List format {{itemData:"L1", itemNum:1, levelNum:1}, {itemData:"L2-child of 'L1' (1)", itemNum:2, levelNum:2}}
repeat with i from 1 to (count of addressedList)
tell application "OmniOutliner Professional" to tell document 1
set currItem to item i of addressedList
set rowTopic to (itemData of currItem)
set currRow to make new row with properties {topic:rowTopic}
set currRowId to id of currRow
set currRowLevel to level of currRow
repeat while currRowLevel < (levelNum of currItem)
indent (first row whose id is currRowId)
set currRowLevel to level of (first row whose id is currRowId)
end repeat
end tell
end repeat
end make_outline_from_addressed_list
It’s alive (bwah hah hah)
I recently wrote a recursion as part of a script to transfer my ‘stuff’ from Mori (RIP…) to OmniFocus.
It receives a list with only 1st level entries of the container being processed, and checks whether a list item has children as it is processed.
It recurses when it finds children.
Parameters:
entryList: the items in the Mori container being transferred. Some of them could contain more items.
refOmniNode: OmniFocus container in which to create tasks (a project on first entry).
on processEntries(entryList, refOmniNode)
tell front document of application "Mori"
set currentNode to "" -- store reference to OF task to be created
set taskCount to count entryList
repeat with i from 1 to taskCount
set refMoriTask to item i of entryList
-- call subroutine to transfer this task
-- catch reference to created OF task
set currentNode to my createTask(refMoriTask, refOmniNode)
if entries of refMoriTask is not {} then -- has children
-- call subroutine to get sorted list of children of this task
set children to my listEntriesSorted(refMoriTask)
-- note we're now passing the OF task that just got created
my processEntries(children, currentNode) -- go deeper
end if
end repeat
end tell
end processEntries
Hope this helps.
(I removed some nonessential code)