Tinderbox's loop mechanism using .each() is helpful but one task can be problematic. This where the task is to loop a list of attribute title values and then act upon each attribute in turn.
The basic problem is that if the loop variable is, for example 'X' and current loop value is 'MyString', how to construct the reference $MyString, so as to act on the MyString attribute.
The answer is to use a nested action() call inside the loop, even if that seems counter-intuitive at first sight. For example, consider the task of resetting every attribute in a note's displayed attributes table. The contents of the table are stored in the Set-type system attribute $DisplayedAttributes, which can be iterated with .each():
$DisplayedAttributes.each(X){...};
Now the task is to set X, in each loop, to address the attribute named in the current value using action():
$DisplayedAttributes.each(X){
action("$"+X+"=;")
};
In other words, if X is value 'MyString' the action() is both concatenating some strings:
"$"+"MyString"+"=;"
…and then evaluating the resulting expression:
$MyString=;
Note that:
- eval() will not work in this context.
- the contents of the action() must concatenate to form a complete action code expression.
It may be necessary to nest quotes. Consider a list of String-type values. To set each one to a value of 'test':
$MyList.each(X){
action("$"+X+"='test';");
};
Note that the target value needs to be enclosed in quotes. As the main expression string already uses double quotes, the string needs to use single quotes. It does not matter if you reverse the nesting. The action would work just as well in the form:
action('$'+X+'="test";');
But, do ensure the sets of single/double quotes nest correctly.
The action can use loop variable more than once. Take the last scenario, but this time where all the attributes are Set or List type. Here the code is:
$DisplayedAttributes.each(X){
action("$"+X+"=$"+X+"+'test';");
};
…i.e. creating an action expression, in-loop, like this for X value 'MyList':
$MyList=$MyList+'test';
White space in the expression does not matter, as in normal action expressions, but is omitted above for brevity/clarity. The expression could also be:
$MyList = $MyList + 'test';
…and still work.
Constructing references to values in other notes
The task gets more complex when one or both of the left/right side attributes is not this
note. Consider this task:
$MyString("Some note")=$MyString("Other note");
… but in a loop where 'MyString ' is a loop variable value. action()
is still the approach. However, to avoid confusing the action code parser with too many sets of quotes—whether on one or both sides of the expression—is to pass the offset agument via a variable. This avoids normal consideration of quoting (paths) or not quoting (anything other than paths) the offset values.
For example, consider the task of iterating Displayed Attributes to set the value of each such attribute in "Some note" into those of "Other note":
var:string vSource = "Source Note";
var:string vDest = "Destination Note";
var:string vCmd;
$DisplayedAttributes(vSource).each(anAttrib){
vCmd = '$'+anAttrib+'('+vSource+')=$'+anAttrib+'('+vDest+')');
action(vCmd);
};
Or, in function form:
function copyDA(iSource:string,iDest:string) {
var:string vCmd;
$DisplayedAttributes(source).each(anAttrib) {
if(dest==""){
vCmd ="$"+ anAttrib +"=$"+ anAttrib +"("+iSource+");";
}else{
vCmd ="$"+anAttrib+"("+iDest+")=$"+anAttrib+"("+iSource+");";
};
action(vCmd);
}
};
// called via code
copyDA("/Notes/Source Note","/Notes/Destination Note");
// or
copyDA("/Notes/Source Note","");
In the code calling the function, the source/destination string might themselves be attribute values or variables:
copyDA($Name(parent),"");
Testing constructed code strings
If encountering errors, it may be useful to incorporate a show() command, which can then be commented out or deleted when all is working. For example:
var:string vSource = "Source Note";
var:string vDest = "Destination Note";
var:string vCmd;
$DisplayedAttributes(vSource).each(anAttrib){
vCmd = '$'+anAttrib+'('+vSource+')=$'+anAttrib+'('+vDest+')');
action(vCmd);
show(vCmd);
};
That will display, in turn, the literal value of vCmd string for each attribute in Displayed Attributes in turn.
A similar but different approach is to log the data by writing it to the $Text of another note, in this case a note 'log' at root level:
var:string vSource = "Source Note";
var:string vDest = "Destination Note";
var:string vCmd;
// clear the log of old data
$Text(/log) =;
$DisplayedAttributes(vSource).each(anAttrib){
vCmd = '$'+anAttrib+'('+vSource+')=$'+anAttrib+'('+vDest+')');
action(vCmd);
$Text(/log) += vCmd;
};