Author: Tomas Rutkauskas
Address Sort Order Index
Answer:
The custom sort order is used to deal with the fact that the house and flat numbers
are sorted as strings. They are stored as strings to allow things like '150175' as
a house number, or '3a', or perhaps even simply a flat 'A'.
The need for a custom sort order is caused by the fact that with an ordinary ASCII
sort order '4' will appear after '30'. This is not desirable behaviour.
This approach to fix this problem is to look for the first number in the string
(if there is one) and then use this as some kind of primary sort order. The rest
of the sorting will then be done on the remaining characters (with preceding and
trailing spaces stripped out), based on the ASCII value of their upper  case
varients. Potential problems caused by this approach include (but are not limited
to) the use of accented characters will
possibly cause strange orderings and furthermore, if there is a block of flats with
three floors A, B, C for example then supposing the flats on those floors are A1,
A2, A3, B1, B2, B3 then the ordering of records will not be ideal  this approach
will sort them as A1, B1, A2, B2, A3, B3. This behaviour is regrettable, but
acceptable  we cannot tell that it is not flat A on floor 1 for example. It's
unlikely that we will be able to find a sort order that always produces ideal
results.
Some examples of sorted lists (not all ideal):
EXAMPLE 1 EXAMPLE 2 EXAMPLE 3
Flat 1 1 A
Flat 2 2 B
3 24 C
3B 3a 1
Flat 3A 5 2
1 unit AddrSortOrder;
2
3 interface
4
5 uses SysUtils;
6
7 function CalcSortIndex(NumStr: string): double;
8
9 implementation
10
11 function CalcSortIndex(NumStr: string): double;
12 var
13 strlength, i, j, tmp: integer;
14 found: boolean;
15 numpart, strpart, divisor: double;
16 choppedstr: string;
17 begin
18 //This function will return the sort index value for the string passed
19
20 strlength := length(NumStr);
21 if strlength = 0 then
22 begin
23 result := 0;
24 exit;
25 end;
26
27 found := false;
28
29 //split the string into a 'number' and a 'string' part..
30
31 //initialise
32 choppedstr := numstr;
33 numpart := 0;
34
35 //Locate the first digit (if there)
36 for i := 1 to strlength do
37 begin
38 if numstr[i] in ['0'..'9'] then
39 begin
40 found := true; //First digit found!!
41 break;
42 end;
43 end; //for i..
44
45 if found then
46 begin
47 //now get the to the end of the digits..
48 found := false;
49 for j := i to strlength do
50 begin
51 if not (numstr[j] in ['0'..'9']) then
52 begin
53 found := true; //end of digits found
54 break;
55 end;
56 end; //for j..
57
58 //Separate out the string parts
59 if found then
60 begin
61 //Number was embedded..
62 val(copy(numstr, i, j  i), numpart, tmp);
63 Delete(choppedstr, i, j  i);
64 end
65 else
66 begin
67 //Number went to the end of the string
68 val(copy(numstr, i, strlength), numpart, tmp);
69 Delete(choppedstr, i, strlength);
70 end;
71 end;
72
73 choppedstr := Uppercase(trim(choppedstr));
74 strlength := length(choppedstr);
75
76 //evaluate a number for the remaining part of the string
77 strpart := 0;
78 divisor := 1;
79
80 for i := 1 to strlength do
81 begin
82 divisor := divisor / 256;
83 //convert from Char to single using a variant conversion
84 strpart := strpart + (ord(choppedstr[i]) * divisor);
85 end;
86
87 //All done, return the value
88 result := numpart + strpart;
89 end;
90
91 end.
