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.