Ordering $MySet

I have an agent that’s pulling a bunch of quotes and in the $Text of the agent, I’m generating a bulleted list of the authors of those quotes, which in this case is stored as a number in the custom attribute $PID.

^action($MySet=collect_if(descendants(this),$Prototype=="Data Extract",$PID);$MySet=$MySet.format("<ol>","<li>","</li>","</ol>");)^
^value($MySet)^

Is there a way to sort this bulleted list in ascending order?

You can’t sort a set; the ordering of sets is up to the computer. (Sets have to be able to answer the question “is X already in the set” very often, so it’s important for them to do this efficiently.)

But you can assign the value of a set to a list.

$MyList = $MySet

Lists can have duplicates. If you add an item to a list, there’s no need to check whether the item is already on the list. (Checking whether an item is in a list is O(n): you might have to look at every item already in the list. Checking whether an item is in a set is O(log n). So, it’s fine to sort lists.

1 Like

Thank you!

To folks following along at home, here’s what I ended up with that gives me the list I want:

^action($MySet=collect_if(descendants(this),$Prototype=="Data Extract",$PID);$MyList=$MySet;$MyList=$MyList.sort.format("<ol>","<li>","</li>","</ol>");)^
^value($MyList)^

1 Like

Let’s tidy a bit…

  • collect() and collect_if returns a listing (actually a List, FWIW).
  • We can be de-dupe (.unique) and sort (.sort)a List-type list

So:

^value((collect_if(descendants(this),$Prototype=="Data Extract",$PID)).unique.sort.format("<ol>","<li>","</li>","</ol>"))^

Get us there without caching lots of list data in $MyList and $MySet. The extra parentheses around the collect call, i.e. (collect()).unique rather than collect().unique, is just to ensure Tinderbox properly evaluates the collect output before we start the chained actions.

As noted, I’m just ‘tiding’ here to avoid using caching attributes and further showing the upside of chainable dot-operators. As you code above works, there is no need to change it. However, in your original code, I’d suggest adding this at the end (bottom) of your template:

^action($MyList=;$MySet=;)^

That way, after the template has run, the two attributes you use are re-set to a default (empty) value. The attributes only store data during the export and avoid saving unnedded data for the long term.

1 Like

Beck! Your on fire! Very cool.

1 Like

Wonderful, @mwra, as always thank you!

The bigger part is getting the right output - as you did. :slight_smile: The ‘tidying’ is just fun and more clarity for when you come to read the code many months later and avoid a “what the …?” moment trying to figure out what it means.

It took me a while to ‘get’ chained actions. When using them just check the order, things that need to be done first, come first. So here, the order of .sort and .unique don’t really matter, but .format() must come last.

Seth Godin put out a piece on this exact topic this morning. :slight_smile:

The order of operations

If you put the jelly on before the peanut butter, the sandwich will fail.

And if you try to spread the peanut butter on the plate and then add the bread, it will fail even worse.

Like so many things, the order is not optional.

And yet, we often do the least-scary or easiest parts first, regardless of what the order of operations tells us.

1 Like