Ah, this code looks familiar. No, they arenât attributes, they are loop variables (as documented). But, first letâs tease the formatting out a bit for clarity (Iâve added an extra line (explained below):
$MyString =;
$MyList = values("Keywords").isort();
$MyList.each(aValue){
$MyString = $MyString + "\n\n" + aValue;
$MyList1= find($Keywords.icontains(aValue));
$MyList1.each(aKeywords){
$MyString = $MyString + "\n" + $ZettelNumber(aKeywords) + " " + $Name(aKeywords);
};
};
$Text = $MyString;
$MyString =;
So, weâve two nested List/Set.each(LoopVar) loops. The loop variable used within the loop is as set by the user. So in the inner loop it is aKeywords
and in the outer loop aValue
. In each case the variable is a place holder for the list item being processed. On the first loop it is list item #1, then next loop #2, until done.
As there are nested loops, every inner list item is processed for every outer list item. So if the out list is 3 items long and the inner 6 items long, there are 3 x 6 iteration before we leave the loop.
Here, the end point is creating $Text (replacing any existing text). We need to run a process to get that outcome so we need somewhere to accumulate the text as we go. A String attribute makes sense and we might as well use $MyString to save bothering making a new attribute. But by all means make your own user String attribute, replacing all instances of MyString
with your attributeâs name (taking care to preserve the $-prefixes). The same goes for $MyList, but as weâve two loops (two lists to process) we will need a second List-type attribute so make MyList1
. Again supply List attributes of your own name is you prefer.
Line 1. We start by ensuring MyString
is empty by setting it to nothing.
Line 2. We use MyList
to stored a sorted keyword list. First values()
returns a list of all unique values for attribute Keywords
. The chained .sort
command alphabetically. The sorted list is passed to MyList
.
Line 3. We start the outer loop based on the list stored in MyList
and set a loop variable name of aValue
for the current list item.
Line 4. We concatenate ( i.e. join to existing value) the current MyString
value (which is nothing at start of first loop) with two line return (ânew lineâ) characters and then concatenate the value of the current list item, i.e. the current value of aValue
.
Line 5. Using the current value of aValue
we make a new list by finding all notes whose Keywords
contains the exactâ value of aValue
. This results in a list of those notesâ Path
data being stored in MyList1
â Note: for lists and sets, .contains can only do exact matches to complete discrete list values and canât use regex.
Line 6. We start the inner loop based on the list stored in MyList
and set a loop variable name of aKeywords
for the current list item.
Line 7. We continue to concatenate to MyString
as in the outer loop, adding a line break character, the ZettelNumber
value for the note at the path currently stored in aKeywords
, then a space character and finally the Name
of the note at the path currently stored in aKeywords
.
Line 8. We close the inner loop. The loop runs until all items in MyList1
have been processed.
Line 9. We close the outer loop. The loop runs until all items in MyList
have been processed.
Line 10. All the loops are complete and all the collected data is in attribute MyString
. We simply set $Text
to $MyString
. Why not just use $Text from the get-go? You could but this makes it easier to pass the outcome to any attribute and designs change. There is no real overhead, though as this is in an edict, perhapsâŚ
Line 11. This reprises Line 1. If the above were a rule, this code would run near continuously (not ideal as there is a find() in there that has to search every one in the doc for every inner loop). So we use an edict. In which case once weâve set $Text, we donât need to stored the data twice. Thus we can âemptyâ MyString
until needed again. Sure, you donât strictly need Line 1 any more but itâs a good belt and braces and has no overhead.
So you will want to add a line break and NoteURL
for the path in aKeywords
to the inner loop (Line 7.) with code like so: + "\n" + $NoteURL(aKeywords)
. However it is probably better to add it as a separate code line so you can see whatâs happening (so be need a bit more concatenation):
$MyString =;
$MyList = values("Keywords").isort();
$MyList.each(aValue){
$MyString = $MyString + "\n\n" + aValue;
$MyList1= find($Keywords.icontains(aValue));
$MyList1.each(aKeywords){
$MyString = $MyString + "\n" + $ZettelNumber(aKeywords) + " " + $Name(aKeywords);
$MyString = $MyString + "\n" + $NoteURL(aKeywords);
};
};
$Text = $MyString;
$MyString =;
We could equally have used a single line:
$MyString = $MyString + "\n" + $ZettelNumber(aKeywords) + " " + $Name(aKeywords) + "\n" + $NoteURL(aKeywords);
âŚbut I think the two line form is clearer.