I believe it’s not a big problem, but I just can’t solve it.
I want to sort tiddlers by number of child tiddlers. Specifically, this is a wiki about my OCs, there are tiddlers of OC (tagged “OC”), and tiddlers of scanned image pages (tagged “imagepage”, and names of all mentioned OCs in that image (same as the OC tiddler title)). I want to know how many imagepage each OC have.
Hello and welcome! Something like this should work — but modify as needed, and feel free to ask follow-ups:
<$let maxx={{{ [tags[]!is[system]] :map[tagging[]count[]] +[maxall[]] }}}>
<!-- the variable <<maxx>> (name it however you like!) marks the high-end of the possible count of tag-children, in your wiki. Its definition works by getting a list of non-system tags, and then using :map operator gets the count of each one, then selecting the maxall of those numbers. -->
<$list filter="[range<maxx>,[1]]" variable="descending-count">
<!-- starting with that maxx variable (highest count for tag-children), we count downward to 1, using range operator). Define <<descending-count>> to track the number we're at in this downward sequence… -->
<$list filter="""[tags[]!is[system]sort[]] :filter[tagging[]count[]match<descending-count>]""" variable="TagAtThisCount">
<!-- For the number we're currently at, we show tags whose count matches that number -->
<$tiddler tiddler=<<TagAtThisCount>> > <<tag>> <<descending-count>> </$tiddler><br>
<!-- For each tag, display whatever details you want... -->
</$list>
</$list>
</$let>
There are surely ways to make this more efficient to run, if you’re running into routine performance issues.
ALSO: I think there’s one edit I had made inconsistently on the code initially posted above. I’ve tweaked it, and it should be more efficient now. Check again, and see if it’s any smoother?
Working on your question, I had the intuition that maybe the input for your subsort filter might be incomplete. I confirmed it with my test data, so maybe you can try this:
This works more closely with OP’s details and begins with their approach (while I just quickly ported over a solution of my own from another wiki)!
I often forget that I can define with <$vars …> and <currentTiddler>outside the scope of where it’s actually getting applied, knowing that the context of application (inside a list widget) determines what means when the variable is invoked there.
Well, to be honest, I started by not reading OP’s code, because their question inspired me a solution with a :sort filter run prefix and a function, but it didn’t work immediately. I had to add the all[tiddlers] prefix to make it work.
Then only I realized it might be OP’s problem too, hence my answer.
This comes in handy when it’s difficult to pass a variable as a macro/procedure parameter inside a widget like <$button actions="""<<mymacro>>""" ...> for example.
Another approach is <currentTiddler>tagging[] — which will capture shadow tiddlers as well. For a tag such as $:/tags/Stylesheet or $:/tags/ViewTemplate sometimes I really want to have a sense of ALL the things with that tag, but starting with all[tiddlers] actually leaves out the shadow tiddlers…
Here it counts the number of times the current tiddler is used as a tag.
Of note is the additional sort[] which ensures items with the same number of tags appear in alphabetical order.
My research suggests there may be 3 or 4 ways to achieve similar results, I am interested in identifying the most intuitive thus easiest to use and share.
Thank you, Tones!
It worked, and was much quicker than what we previously got.
However I wasn’t quite sure where to add the “tag[imagepage]” part in these neatly written filters. (The filter here is supposed to count all child tiddlers, not only those with an “imagepage” tag, …right?)
Replace that segment [tag[TableOfContents]sort[]] with whatever your desired filter is, such as tag<currentTiddler>tag[OC] … … sort[]]. (And you can omit that ending sort[] step, since there’s a sort coming up in the next filter run.)
And in the functions themselves — if you ALWAYS just want counts for the imagepage-tagged stuff, then:
FYI: I added this additional sort to put the items in alphabetical order before the subsequent sort via a count. Sometimes a count will come up with the same number for more than one title, this will result in it sorting alphabetically within each matching count.
I knew this was possible but I have struggled to find good working examples. Thus in filters sort1 > sort2 will result in items sorted by sort2 and within that sort1.
This is a very useful technique. It’s much, much easier than trying to do a complex sort by count-then-alphabetical-order in one go. A decade ago it wasn’t guaranteed, but the 2015 JS standard required that sorts be stable, and so now a double-, triple-, or multiple-sort is easy to achieve by running the sorts in reverse priority order.