Sort by field x and then by field y

Hi, I would like to display all tiddlers with a certain tag and sort them primarily by the field course. Many tiddlers share the same field value for course and I want to sort these by the field section.

I use the following codes:

<table>
  <tr>
		<th>Title</th>
		<th>Course</th>
		<th>Section</th>
	</tr>
<$list filter="[tag[ClassSection]] +[sort[course]] ">
  <tr>
	  <td>
		  <$link/>
		</td>
		<td>
		  <$transclude field="course"/>
		</td>
		<td>
		  <$transclude field="section"/>
		</td>
	</tr>
</$list>
</table>

and this is what I have for my data:
Screenshot 2021-10-22 092753

As you can see, the course MATH100 does not sort right (for what I need). I’m not sure how TW sorts things when it sees two or more tiddlers with same field value. (I’m guessing by creation field?)

Is there a way to specify the secondary sort field? For example, it would be nice if we can do something like this (for the nonexisting operator multisort).

[tag[ClassSection]] +[multisort[course,section]]

Any help is appreciated.

Edit: Add data tiddlers for testing class-sections.json (3.6 KB)

Hi,

This is a little bit hard to do without sample tiddlers to work with, and I’m already past my bedtime , so no time for me to enter a decent number of tiddlers for testing.

That said, the following seems to work for me with three sample tiddlers I quickly entered. See if a modified version (i.e. modified to work for your tiddlers) works for you:

(this does require courses to have a standard/consistent number of characters; the “enlist” is just my quick and dirty way of getting my tiddlers)

<$vars thisSub="[{!!course}addsuffix{!!section}]">

{{{ [enlist[a1 a2 a3]sortsub<thisSub>] }}}

</$vars>
1 Like

Hi @Charlie_Veniot, sorry for not giving sample data at first. I removed some private information and posted the sample data in the original post.

I’m a little confused with your code. How do I apply it to my data?

You are getting what you ask for here. All tag ClassSection sorted by the course field.

<$list filter="[tag[ClassSection]] +[sort[course]] ">
   <$link/> {{!!course}}<br>
</$list>

What you really want is to sort by Class Section and within those the sort the class?. A strait forwards way to do that is to nest lists.

<$list filter="[tag[ClassSection]each[section]get[section]sort[]]" variable=section>
   <$list filter="[tag[ClassSection]section<section>sort[course]]">
      <<section>> {{!!course}} <$link/> <br>
   </$list>
</$list>
  • The first identifies all the possible “section values” and sorts them alphabeticaly, due to the each we will get one value per section.
  • The second list again finds tiddlers tagged [tag[ClassSection] but only with the current section value, and sorts using the course value.
  • So for each section we list tiddlers by their course value.
2 Likes

@Charlie_Veniot
As for thissub, I would suggest to alter it to

<$vars thisSub="[{!!course}addsuffix[   ]addsuffix{!!section}]">

The 3 spaces I add between the two should be sufficient to be sure that first field will always be preponderant over second field. I choose space as it it the lowest ascii code beyond control chars. One could add return carriage too (a single one is enough in the context of single line field).

illustrted goal:

|first field|second field|
|-----------------------------|
|aga|zuh|
|agam|tubi|

agazuh is second to agamtubi but this is not what we want.
whereas “aga zuh” is first to “agam tubi” as we need.

1 Like

Applying Charlie’s code (with @jypre’s addition) to your example would look something like this:

<table>
  <tr>
		<th>Title</th>
		<th>Course</th>
		<th>Section</th>
	</tr>
<$vars thisSub="[{!!course}addsuffix[   ]addsuffix{!!section}]">
<$list filter="[tag[ClassSection]sortsub<thisSub>] ">
  <tr>
	  <td>
		  <$link/>
		</td>
		<td>
		  <$transclude field="course"/>
		</td>
		<td>
		  <$transclude field="section"/>
		</td>
	</tr>
</$list>
</$vars>
</table>
2 Likes

I think this called doubly sorted table! You can convert this to a macro and for example sort in ascending order by first column and descending order for second column!

A little improvement if you have numbers only in second col

<$list filter="[tag[ClassSection]sortsub:alphanumeric<thisSub>] ">
2 Likes

Wow! Thank you so much everyone! I now understand what @Charlie_Veniot originally suggested (and all following improvements).

<$vars thisSub="[{!!course}addsuffix[   ]addsuffix{!!section}]">
filter="[tag[ClassSection]sortsub<thisSub>] "
</$vars>

So the trick to do doubly sort by field x and y (and perhaps more) is to concatenate these field values (value of x)(value of y) and then sort this concatenation as string, or number. This is clever. Thank you!

Note that you can also use a filter run prefix and avoid the vars widget:

[tag[ClassSection]] :sort:alphanumeric:casesensitive[{!!course}addsuffix[ ]addsuffix{!!section}]

More examples in the documentation.

It would be great if someone was up for adding an example to the documentation of sorting by a combination of two different fields.

2 Likes

This is amazing! I always love single $widget approach! conditional expression in one filter pass with several run steps!

1 Like

@Mohammad it is pretty neat, isn’t it?

Both approaches have their own benefits. The plus with the filter operators like sortsub[], filter[] etc that accept a filter is that you can use multiple filter runs for sorting or filtering for even more complex logic!

1 Like

It is really wonderful!
I like to find some spare time and prepare a bunch of examples using these new features in TW 5.2.0!

3 Likes

Good stuff!

I had kept my original code slightly simpler by assuming course and section codes would be consistent sizes.

For a generic version of this code to work with anything, consider adding “pad” to whatever number of fields involved in sorting. A “pad[20],[a]” or something like that would do the trick, replacing that “20” with something that is maybe 10-20 characters bigger than the max number of characters you might expect for that field.

Something like that.