Re: Sorting? [sorted]
Re: Sorting? [sorted]
- Subject: Re: Sorting? [sorted]
- From: has <email@hidden>
- Date: Thu, 12 Sep 2002 19:44:09 +0100
Goran Ehn wrote:
>
Does anyone have a sorting routine that sorts name/numbers correctly?
>
>
I used the sorting routine at
>
http://www.apple.com/applescript/guidebook/sbrt/pgs/sbrt.05.htm
What an underwhelming... lump... of code, but let's work with it for now,
shall we? <grits teeth>
Okay, one word here: callbacks. But more on that later. First things first:
your definition of "correctly" and the computer's aren't the same. When
comparing two strings, AS simply checks each character against its opposite
until it finds two that are different, thus in:
box10
box2
it compares characters "b", "o", "x" and finds them the same in both; then
compares "1" and "2", and finds that "1" is less than "2", so replies that
"box10" < "box2". This behaviour is 100% correct. But whether it's what you
actually _wanted_ is another question.
--
Since AS has no way of knowing you want to look at that part of the string
differently, you're going to have to do the extra work yourself. There are
two ways [1] of approaching this task:
1. you take your existing sort routine and mangle it to make it to do this
special-case sort of yours
2. you take your existing sort routine and move the comparison part out of
the sort handler and into its own handler. You then modify the comparison
handler alone.
Option 2 is so much the better option, I hardly think it needs pointing
out, but I'll do so anyway. If you pursue option 1, great: you'll have a
handler that does your special sort, but which is quite unable to do any
sort of other sort. Also, you'll have mixed up your complex, custom
comparison code with the the standard sorting code, making both harder to
understand and maintain. You want to avoid over-specialising your code like
this otherwise it's real easy to end up with vast amounts of hugely
complex, quite unreadable code after a while.
Instead, tease out the specialised part (the comparison) from the general
part (the rest of the sort), and treat the two separately. That way, you
can focus all your attention on writing your custom comparison code, and
ignore the rest of the sort routine completely. And the simplest way to do
this is to move the comparison code into its own handler, and let the sort
routine call that handler every time it needs that comparison done.
--
So, here's the Guidebook sort routine [2], only I've modified the line that
originally compared the two strings. The code that figures out how to
perform the comparison and does it, is now contained in the isLessThan()
handler. For this simple example, I've assumed that only the number after
"page1_box" ever changes, but now that the comparison code is isolated like
this it'll be dirt easy to get in and change it if you need to compare your
values a different way.
======================================================================
--generalised sort code
on ASCIISort(myList)
set indexList to {}
set sortedList to {}
repeat (number of items in myList) times
set lowItem to ""
repeat with i from 1 to (number of items in myList)
if i is not in indexList then
set thisItem to item i of myList as text
if lowItem is "" then
set lowItem to thisItem
set lowItemIndex to i
else if isLessThan(thisItem, lowItem) then -- CHANGED!!!
set lowItem to thisItem
set lowItemIndex to i
end if
end if
end repeat
set end of sortedList to lowItem
set end of indexList to lowItemIndex
end repeat
return sortedList
end ASCIISort
--specialised comparison routine
on isLessThan(val1, val2) -- [must return true/false]
set val1 to (text 10 thru -1 of val1) as integer
set val2 to (text 10 thru -1 of val2) as integer
return (val1 < val2)
end isLessThan
======================================================================
Now, I mentioned callbacks before, and I don't really want to disappear
into an in-depth discussion of those too. Suffice it to say, they allow the
sort routine to take your custom comparison routine as an additional
parameter, which means you can use the same sort routine with _any_
comparison routine you like: you just feed it your comparison routine along
with the data and, Whoosh!, away it goes.
Because of some implementation crappiness in AS, you have to wrap your
comparison routine inside a script object: you can't just feed the handler
direct to the sort routine [3]. But don't sweat it, it's no biggie. Below's
the code; I'm sure you can figure out how I've changed it from the previous
version yourself:
======================================================================
--sort routine
on ASCIISort(myList, comparisonObj)
set indexList to {}
set sortedList to {}
repeat (number of items in myList) times
set lowItem to ""
repeat with i from 1 to (number of items in myList)
if i is not in indexList then
set thisItem to item i of myList as text
if lowItem is "" then
set lowItem to thisItem
set lowItemIndex to i
else if comparisonObj's isLessThan(thisItem, lowItem) then
set lowItem to thisItem
set lowItemIndex to i
end if
end if
end repeat
set end of sortedList to lowItem
set end of indexList to lowItemIndex
end repeat
return sortedList
end ASCIISort
--
--comparison objects
script simpleComparisonObj
on isLessThan(val1, val2) -- must return boolean
return (val1 < val2)
end isLessThan
end script
script page1_boxComparisonObj
on isLessThan(val1, val2)
set val1 to (text 10 thru -1 of val1) as integer
set val2 to (text 10 thru -1 of val2) as integer
return (val1 < val2)
end isLessThan
end script
-------
--TEST
set the theList to paragraphs of "page1_box0
page1_box1
page1_box10
page1_box11
page1_box12
page1_box13
page1_box14
page1_box15
page1_box16
page1_box17
page1_box18
page1_box19
page1_box2
page1_box20
page1_box21
page1_box22"
ASCIISort(theList, simpleComparisonObj)
--> {"page1_box0", "page1_box1", "page1_box10", "page1_box11",
"page1_box12", "page1_box13", "page1_box14", "page1_box15",
"page1_box16", "page1_box17", "page1_box18", "page1_box19",
"page1_box2", "page1_box20", "page1_box21", "page1_box22"}
ASCIISort(theList, page1_boxComparisonObj)
--> {"page1_box0", "page1_box1", "page1_box2", "page1_box10",
"page1_box11", "page1_box12", "page1_box13", "page1_box14",
"page1_box15", "page1_box16", "page1_box17", "page1_box18",
"page1_box19", "page1_box20", "page1_box21", "page1_box22"}
======================================================================
Now you can feed in whichever comparison [object] you like when sorting
your data. Very cool, and makes you feel really powerful too.:)
Needless to say, you can use these techniques in modifying any type of sort
routine (bubble, insertion, shell, quicksort, etc). e.g. My personal
collection of Really Handy Scripts contains both a plain quicksort (for
when I need nothing fancier) and a callback-based one. I just pick up the
latter whenever I need to sort a list of lists on the third item of each
list, say, write a suitable comparison object to compare the third items of
each list [five new lines of code, tops], and I'm sorted. Could not be
easier.
HTH
has
[1] Okay, three options if you count rolling a completely non-standard,
highly specialised piece of code from scratch [JD's?], but I really can't
recommend that approach: you want to make your life _easier_, not harder.
[2] OK, so I also cleaned it up bit for readability, but that's just
cosmetic changes.
[2] Yes, small white lie. Yes, for your own good. No, I don't want to argue
it.:p
--
http://www.barple.pwp.blueyonder.co.uk -- The Little Page of AppleScripts
_______________________________________________
applescript-users mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/applescript-users
Do not post admin requests to the list. They will be ignored.