Tinderbox v10 Icon

eachLink(loopVar[,scope]){actions}


Operator Type: 

Operator Scope of Action: 

Operator Purpose: 

Data Type Returned: 

Operator First Added: 

Operator in Current Baseline: 

Operator Last Altered: 

Operator Uses Scoped Arguments: 

Operator Has Optional Arguments: 

Operator Uses Loop Variable Argument: 


eachLink(loopVar){action(s)}

The eachLink() operator examines each link for the current note, either inbound or outbound; prototype links are excluded. The local, user-named, variable loopVar argument is bound to a dictionary-type object of per-link properties, and is used in the {}-enclosed action code.

.eachLink() includes 11 new read-write keys in the dictionary it builds for each link. These first two can be useful when several notes in a container have identical names and the remaining new keys cover various display aspects of the link. The per-link Dictionary-type data comprises the following keys. All are editable except item in italics:

Another way to understand the keys, is in terms of their general purpose:

For editing key values, see section 'Editing link properties' below.

Note that it is not possible to read a link target's anchor text (i.e. for a link linking to a $Text selection) via eachLink(). Currently such data is only observable via the source XML of the document.

eachLink() examines each link in the sequence as seen in the listing in Browse Links…, so it examines text links is the order their anchor text appears within $Text.

Link Counts

The count of the links iterated for a given note by eachLink() is $OutboundLinkCount plus $InboundLinkCount:

var:number vLkCt = $OutboundLinkCount + $InboundLinkCount; 

First and Last item tests

Although eachLinks() is functionally like a list iterator (i.e. List.each()), it works off a dynamically generated List but the loopVar is bound to each list item's Dictionary. This means the list object properties of List.count, List.first and List.last are not available. However, the per-link Dictionary object offers two special keys:

In effect these keys mimic, for eachlink(), the .first() and .last() tests as used by .each(). This the following example the first conditional if() test uses the long form test for a boolean true, the second the short form—both give the same result:

	eachLink(aLink){
		if(aLink["isFirst"]==true){
			//this is the first link in the listing;
		};
		if(aLink["isLast"]){
			//this is the last link in the listing;
		};
	};

Why might this be used? If the links are being processed so as to only export certain typed links, it may be necessary to add additional text/styling before the first and after the last link.

Detecting basic vs. text vs. web links

The 3 types differ in that:

These conditions can be tested for when iterating a note's links

	eachLink(aLink){
		if(aLink["url"]!=""){
			// this is a web link
		}else{
			if(aLink["anchor"]!=""){
				// this is a text link (has anchor but no url)
			}else{
				// this is basic link (has neither anchor nor url)
			}
		}
	};

Or as a function:

	function fWhatSortOfLink(iAnchor:string, iURL:string){
		if(iURL!=""){
			return "web";
		}else{
			if(iAnchor!=""){
				return "text";
			}else{
				return "basic";
			}
		}
	};
	var:string vTypeOfLink;
	eachLink(aLink){
		vTypeOfLink = fWhatSortOfLink(aLink["anchor"],aLink["url"]);
			// do something depending on the type of link
		};

More examples

Filtering inbound vs. outbound links (outbound links share the same $ID as the note whose links are being read):

	function fLinkDirection(iIdNote:string, iIdLink:string){
		if(iIdNote == iIdLink){
			return "outbound";
		}else{
			return "inbound";
		}
	};
	// called like so
	$MyString = vTypeOfLink = fLinkDirection($ID,$ID(aLink["source"]));

Does this note have a link of type "agree"?

	function fIsAgreeable(){
		eachLink(aLink) {
			if(aLink["type"]=="agree"){
				return true;
			};
		};
		return false;
	};

Or, to count the number of links from this note to tasks:

	function fLinkedTasks(){
		var:number vCount=0;
		eachLink(aLink){
			if($Prototype(aLink["destination"])=="Task"){
				vCount += 1;
			};
		};
		return count;
	};

The examples use line breaks for clarity, and the last example may equally well be stored and used without line breaks, though still taking care to delimit/terminate discrete expressions with semicolons

The latter might make sense if trying to use a function within something like a rule, but if the code is defined in a Library note within Hints, then there is little gain in a one-line approach as it can be hard to read.

eachLink(loopVar,scope){action(s)}

An optional second argument for eachLink() allows designating the note whose links are to be examined, using a path or designator as the scope argument. Previously, eachLink() was explicitly bound to the current note.

For example,:

	eachLink(aLink, parent){
		… // action code here
	};

performs an action on each of the links to and from the parent of the current note (i.e. this note).

Note that scope is a single item; if needing to work a list of notes, use a nesting List.each() with the outer loop passing a single scope item to eachLink(). Consider the following:

	$MyList.collect(children,$ID);
	$MyList.each(anID){
	   eachLink(aLink,anID){
	      // do stuff on the links of the note defined by current anID's $ID
	   }
	};

The latter might as easily be done with a function, that takes $ID as an input and wraps the eachLink() loop using the passed-in ID as the eachLink() second argument.

Where the designator argument would be this, it may be omitted as the original usage eachLink(aLink){…} automatically assume the context of the action performed on each link to and from this note.

Editing link properties

In eachLink() loops, most properties of the link are editable (at least, from v9.6.0). For example, to change any of the current note's 'untitled' links to link type 'reference':

	eachLink(aLink){
		if(aLink["type"] == "*untitled"){
			aLink["type"]  = "reference";
		}
	};

If the link type being set does not already exist, it is created and applied. But, what if the note to be checked is not the current note? Consider working via an agent, where the current note is an alias. In that case, it is necessary to use the optional scope argument and additionally rather than specify a note name or path to use the original designator. The same task as above now becomes:

	eachLink(aLink,original){
		if(aLink["type"] == "*untitled"){
			aLink["type"]  = "reference";
		}
	};

the only change being the extra use of the scope argument to define the correct context for evaluating the links.

To change the link type of any link of type 'disagree' to 'agree', within the eachLink() loop use:

if(aLink["type"]=="agree"){aLink["type"]="disagree";}; 


See also—notes linking to here: