Tinderbox Forum

How to perform a Delete action on a note that fulfils certain conditions?

I have the following action code, saved as a Stamp. It looks for notes inside the “/Keywords” container that have no text, and moves them into the “/Trash” container.

var keywords("/Keywords");
var trash("/Trash");
var notes;
notes=collect_if(children(keywords), $Text==(),$Path);
notes.each(note){
  $Container(note)=trash;
};

I would like to change it so that instead of moving such notes into “/Trash”, it simply deletes them, using the Delete action operator code.

I had thought that I could simply change penultimate line to delete(note);, but clearly I don’t know what I am doing. I also tried simpler if(condition){action} but to no avail.

Could someone point me in the right direction?

I think I’d avoid note as a loop variable name, it’s just begging to be misinterpreted. If in doubt, use an unlikely-to-be-used prefix, e.g. myNote.

Re delete() - someone once said, “with great power comes great responsibility”. Proceed with caution; use copies until sure of the code. Delete has no undo.

myNoteList.each(myNote){
  delete(myNote);
};

We know myNoteList is a variable holding a list of paths [sic], so this should avoid the problem of matching the right note with non-unique names.

1 Like

I’ve just updated my note’s on delete() to give a usage example and to make the point about using paths to point to the desired target item.

Thank you, Mark. So just to make sure I’ve got it right and started to grasp the syntax…is this how I would write out the whole code?

var keywords("/Keywords");
var myNoteList;
myNoteList=collect_if(children(keywords), $Text==(),$Path);
myNoteList.each(myNote){
  delete(myNote);
};

Looks good. As one tends to write these things then not looks at them for ages, a var name like myNoteList, which reminds you this is a list and not just one value can be a time-saver against head-scratching later on.

I’ve no tested that code, other than verifying the delete() step but the other code looks right and I only changes some variable names. HTH - I’ll catchany follow up tomorrow.

1 Like

It’s working perfectly in my test document. Thank you again, Mark.

1 Like

Instead of futzing with loops, do this in an agent. You can delete(original) in an agent. I’m not sure that delete accepts group designators.

1 Like

In this case, I was using the command in a stamp, in order that deleting be deliberate. But automating would be easier, and in my use case there is no possibility of data loss through delete. I will give this a go. Thank you, Mark B.

Agents are simpler is the task is ongoing/repetitive. A separate aspect is whether the agent(s) don’t hold a lot of aliases. In the latter case try to refine the query so the number of aliases in the agent (as opposed to the number checked by the query) is small. From experience, in a large document lots of agents holding lots of agents begin to gum things up as the aliases also get queried by other agents (i.e. the app has more items from which to check each cycle).

For on-demand/infrequent things I find stamps or edicts a more effective/less intrusive approach. For initial testing and in small sized docs, I’d agree agents are—if nothing else—an easier way to get going. I tend to propose the stamp/edict route where people are describing a project that will grow to size. This avoids them then needing to evolve their agents into stamps/edicts as the document matures.

1 Like

Thanks, @mwra. I’ll keep that in mind as the file grows. For now I’ve implemented Mark B’s advice, but kept the stamp based on the loop in place with an eye for the long run.

I have a follow up question: while I understand how to execute a delete(item) command using an agent, I have been unable to figure out how to do this seemingly easy task using something like an onAdd action.

Let us assume the following top-level containers:

- Notes
- Trash
    - note1
    - note2

I am trying to create an onAdd action (in the Action pane of the Inspector), in which if any note is added to the container ‘Notes’, this executes a delete action on all descendants of the container Trash.

I think my problem is that I don’t fully understand how the ‘item’ within delete(item) is determined if it is not an exact path, but (I assume) some kind of designator.

Is what I am facing a concomitant of what @eastgate meant about group designators and delete?

Why should adding something to Notes have an effect on the ‘trash’? I’m not saying I think it can’t be done, but I don’t want to give an answer until I understand the rationale for doing this; there may be a more sensible way to do this.

A fair question. This itself is not for a real-life use in one of my active TBX files. Rather, it is a small exercise to understand how the syntax of the action code of delete (and item-type action code more generally) is meant to be written to elicit an effect on a designated items.

The TBX Ref File states that delete has an Operator Scope of Action of item. I’m trying to learn how such ‘items’ are (a) designated or defined and (b) are acted upon. And I thought this would be a good, simple test, but I came up empty-handed.

There is no formal definition of item, though I try to use it consistently in aTbRef. By item I mean exactly a single thing, as opposed to a group of one or more things. Those things are usually a normal notes but could be agents (or special notes like adornments or separators). If you insert ‘a single note unambiguously referenced’ for ‘item’ you’ll be on the mark.

Why ‘unambiguously referenced’? In most simply example items are described using their title ($Name). This is unambiguous if the name is unique (and in some cases if there are no aliases). A full path ($Path) is a failsafe. Otherwise, if more than one item matches a $Name, Tinderbox in most (all?) cases will match the first by $OutlineOrder. $Path is thus safer as the full path to the item is supplied.

One ‘gotcha’ in this is that there are a group of, as yet not formally documented, characters that break path-parsing: ampersand, parentheses, semi-colon, forward-slash, straight double-quotes and likely others. These characters are valid in $Name. They only fail in action-code parsing and there is no action code mechanism for escaping these ‘bad’ characters.

If you want to experiment with automated deletion I strongly urge to only work on disposable test files or copies of real docs—and rename the latter so they version under a different name. It is not the the deletion process is faulty, but it offers lots of scope for unforced errors by the user. :face_with_hand_over_mouth:

Thank you for this explanation, Mark. It clarifies what what is mentioned in the TBX Ref File.

Does this entail what I am trying to do — create an onAdd action (or a Rule/Edict with ‘if-then’ action code) that deletes all the descendants of another container — is not possible? Are descendants or children ‘ambiguous’?

Again why? If you don’t know why, you won’t understand if it goes wrong or works unexpectedly. It seems a really odd way to go about this.

‘Descendants’ is moot here. Consider note ‘dd’ at path ‘/aa/bb/cc/dd’. Now if I run this code:

delete("cc");

‘dd’ is lost because in deleting ‘cc’ it and everything it contains is deleted (without an undo).

If you are trying to do something like:

delete(children("bb"));

it doesn’t work, though in theory it might. Designators can take title/path values as offsets:

$MyString=$Name(parent("dd"));

Gives ‘dd’.

But I go back to my earlier point, if you don’t really know why you want to do this task, there are probably more fruitful experiments to do. If you have a use case and think it should work, then mail in a feature request/fix request.

Thank you for the explanation, Mark.

I am just trying to fundamentally understand the mechanics of actions and their objects, be they individual items or groups of items. And I thought something like delete (which I was learning to use for other purposes) would prove a good study of the kind of syntax needed to make an action act upon a set of items.

This particular question should probably have been asked in ‘Q & A Getting Started’ section of the forum. Without the context of an actual task, the question probably does come off as an arrow shot in the dark.

I’d politely suggest that create() and delete(), very recent additions to actions code are absolutely not the best place to start.

As to the mechanics, nearly everything is or derives from an attribute. Queries find things generally by a combination of one/both of attribute values or outline hierarchical relationships. Queries return paths [sic]. Creating/deleting notes is better though of as advanced stuff (a) because of the scope for error and (b) these features haven’t been heavily used so there may so surprises yet at which point I cite (a) again. In short, I’d start exploring action code anywhere but with these two operators. :slight_smile:

1 Like

I believe that the designator for create() and delete() is expected to refer to any one note. It might be:

  • A full path
  • A relative path
  • A keyword designator like this or parent
  • A compound designator, like nextSibling(parent)

The danger here, obviously, is that if you make a mistake, Tinderbox might create or delete an unintended note somewhere you aren’t looking. You might discover that much later! That’s why I resisted repeated requests for a delete() operator. I think it is seldom needed, but sometimes it can be nice to have.

When I’m experimenting, I like to set the $Color of notes. It’s safe, the changes are evident, and if something goes wrong there’s not much harm. $Badge, $Shape, and $Flags can also work well.

3 Likes

Having now experimented with other action code operators, I see that indeed this was the point of my confusion. Delete() works fine on one note, but not on multiple, even when other action operators do.

For those who who may come across this thread in the future:

E.g. if the following is placed in the Action tab (i.e. an $OnAdd value) for Container1:

$MyList=collect(children(/Container2),$Path);
linkFrom($MyList);

links will be created between any new child to Container1 to all the notes in Container2.

However,

$MyList=collect(children(/Container2),$Path);
delete($MyList);

works if there is a single item in /Container2, but not if there are more than one.

EDIT: Thanks to @PaulWalters, I learn now that the following works:

$MyList=collect(children(/Container2),$Path);
$MyList.each(x){delete(x)};