Issue 52 * July 3, 2008

Sorting by ValueList
Sort More with Less Code

by Hugh Senior

Have you needed to sort lists by a value list or synchronize different lists, but thought it not easily do-able in Rev? You may find the following tips useful based on an entry in the Scripter's Scrapbook.

In a field, type some lines where the first word is a random day of the week, then...

on mouseUp
   --| Numeric isn't strictly needed as the number will never
   -- exceed 10,
   --| but it does show that the value for the sort is a number
   sort lines of fld 1 numeric by valueList(word 1 of each) 
end mouseUp

function valueList what
   put \
   "Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday" into tList
   return itemOffset(what,tList)
end valueList

So what is actually going on here?
The clever bit is the word "each" that can evaluate the result of a function on each line first, before doing the actual sort. You can think of it as applying a virtual value (in this case, a number 1 to 7) to each line, then sorting the lines on that. J. Landman Gay explained it this way: "Rev evaluates the function before sorting each line, so what it is actually sorting by is a number in this case. "Each" evaluates to the first word of each line in this case, which the function then re-evaluates as the position in the list of days of the week. So when "each" is "Monday" the sort number is 1. When it's Tuesday, the sort number is 2, and so forth."

As ever, Richard Gaskin makes two good points:

"So this tells me two things:

"1. Using a function for a sortKey expression introduces a "sometimes" rule in terms of understanding the order of expression evaluation in the engine, in which most of the time functions are evaluated first but in this case the function is applied repeatedly for each line of the sort container as the sort command is run.

"2. Using a function as a sortKey expression evaluates the sort container as though by effectively adding data to it, rather than anything necessarily in the data itself."

Of course you are not limited to "sort lines". You can apply any valid chunking expression such as "word 2 to -1 of item 3 of each". And you are not limited to fields either because you can apply the valueList function sort to variables as well. In fact, if you have a lot of data to sort, it will be much faster if you put the field data into a variable, sort the variable, then put the variable back into the field (fields are good places to show information, but are relatively slow to manipulate).

What about lots of value lists? As ever, define things once then re-use them, so store your lists as re-usable custom properties...

function valueList what
   return itemOffset(what,the uDayNames of this stack)
end valueList

What about parallel sorting of multiple fields?
Applying the same logic above to multiple fields (or indeed variables), the following implements parallel sorting of linked fields, keeping the lists synchronized (based on an old HyperCard script by Brett Sher).

Create 3 fields called 'data1', 'data2' and 'data3'. Add lines of text into each field, for example a list of first names, last names, ages, then...

on mouseUp
   sortBy "data1" --| Sort by first name
end mouseUp

local lLineCounter,lKeyData
on sortBy keyField
   put fld keyField into lKeyData
   put "data1,data2,data3" into dataFields --| fill in your own \
   field names here
   repeat with i = 1 to the number of items of dataFields
      put 0 into lLineCounter
      sort lines of fld (item i of dataFields) by key()
   end repeat
end sortBy

function key
   add 1 to lLineCounter
   return line lLineCounter of lKeyData
end key

Further reading:
Try Ken Ray's tip at Sons of Thunder that looks at custom sorting line items.

Main Menu What's New