Lexicographic Ordering
Lexicographic ordering is what the APL primitives ⍋
and ⍒
provide:
⎕io←0 ⍝ ⎕io delenda est
⎕rl←7*5 ⍝ to get reproducible random results
a←?11 3⍴3
a a ⌷⍨⊂ ⍋a
2 1 0 0 1 0
0 2 2 0 2 2
1 1 1 1 0 0
1 0 0 1 0 1
1 1 1 1 0 1
1 2 1 1 1 0
1 0 1 1 1 1
1 0 1 1 1 1
1 1 0 1 2 1
0 1 0 1 2 2
1 2 2 2 1 0
First order by column 0, resulting in groups of rows with the same values in column 0. Then, within each group, order by column 1, getting subgroups with the same values in columns 0 and 1. Then, within each subgroup, order by column 2, getting subsubgroups with the same values in columns 0, 1, and 2. In general, for each subsub…subgroup, order by column k
, getting groups with identical values in columns ⍳k
.
The preceding discourse is descriptive rather than prescriptive—algorithms for ⍋
can use more efficient and more straightforward approaches. As well, for ease of understanding, the description is for a matrix and speaks of columns and rows. In general, any non-scalar array can be ordered, whence instead of rows, think major cells, instead of column, think item in a major cell. Symbolically and more succinctly, ⍋⍵ ←→ ⍋⍪⍵
.
⍋
can be used if the orderings in the process are all ascending, or ⍒
if all descending. The problem to be solved here is where the orderings are a mix of ascending and descending.
Numeric Arrays
Since ⍒⍵ ←→ ⍋-⍵
if ⍵
is numeric, for such ⍵
multiply each descending column by ¯1
and each ascending column by 1
, then apply ⍋
. This induces a “control array” having the same shape as a major cell of the argument, with a ¯1
for descending and a 1
for ascending.
adn←{⍵ ⌷⍨⊂ ⍋ ⍺ ×⍤99 ¯1 ⊢⍵}
For the array a
in the previous section:
a 1 ¯1 1 adn a ¯1 1 1 adn a
2 1 0 0 2 2 2 1 0
0 2 2 0 1 0 1 0 0
1 1 1 1 2 1 1 0 1
1 0 0 1 2 2 1 0 1
1 1 1 1 1 0 1 1 0
1 2 1 1 1 1 1 1 1
1 0 1 1 1 1 1 1 1
1 0 1 1 0 0 1 2 1
1 1 0 1 0 1 1 2 2
0 1 0 1 0 1 0 1 0
1 2 2 2 1 0 0 2 2
In 1 ¯1 1 adn a
, column 0 is ascending, and within that, column 1 is descending, and within that, column 2 is ascending. In ¯1 1 1 adn a
, column 0 is descending, and within that, column 1 is ascending, and within that, column 2 is ascending.
Ordinals
An array to be sorted can be converted to an order-equivalent integer array by assigning to each item an ordinal (an integer) which has the same ordering relationship as the original item relative to other items in the array:
sort ← {(⊂⍋⍵)⌷⍵}
ordinal ← {⎕ct←0 ⋄ ⍵⍳⍨sort,⍵}
That is, the ordinals obtain as the indices of the original array in the sorted list of the ravelled elements, using exact comparisons. (Exact comparisons are used because sorting necessarily uses exact comparisons.)
For example:
⊢ d←¯1 'syzygy' (3 ¯5) 1j2 'chthonic' (¯1)
┌──┬──────┬────┬───┬────────┬──┐
│¯1│syzygy│3 ¯5│1J2│chthonic│¯1│
└──┴──────┴────┴───┴────────┴──┘
ordinal d
0 5 3 2 4 0
In the example, the data items are ¯1
, 'syzygy'
, 'chthonic'
, 3 ¯5
, 1j2
, and ¯1
again. With respect to ordering, these data items are perfectly represented by the ordinals (numbers) 0, 5, 3, 2, 4, and 0, respectively. That is, ⍋d ←→ ⍋ordinal d
.
⍋ d
0 5 3 2 4 1
⍋ ordinal d
0 5 3 2 4 1
As the example illustrates, it is imperative that identical ordinals are assigned to identical items, else the ordering relationships would be changed. For example, if b←0,⍪2 1
and the 0s are assigned different ordinals,
⊢ b←0,⍪2 1
0 2
0 1
ordinal b ⊢ bo←0 3,⍪1 2 ⍝ faux ordinals
0 3 0 3
0 2 1 2
⍋ ordinal b ⍋ bo
1 0 0 1
⍋ b
1 0
Computation of ordinals is greatly facilitated by the total array ordering introduced in Dyalog APL version 17.0.
Non-Numeric Arrays
A general solution for the ordering problem obtains by first converting the array to an order-equivalent integer array through the use of ordinals.
ado ← {⍵ ⌷⍨⊂ ⍋ ⍺ ×⍤99 ¯1 ordinal ⍵}
For example:
⎕rl←7*5 ⍝ to get reproducible random results
x0← ?19⍴4
x1← (⊂?19⍴2) ⌷ 'alpha' 'beta'
x2← (⊂?19⍴3) ⌷ 'abc'
x3← (⊂?19⍴3) ⌷ 'able' 'baker' 'charlie'
x ← x0,x1,x2,⍪x3
ordinal x
13 49 19 42
10 49 32 68
13 49 63 68
4 49 63 42
0 27 19 23
13 49 32 42
0 49 19 42
10 49 32 68
10 27 32 23
4 49 32 68
4 49 32 68
4 27 32 23
4 49 32 68
0 49 63 68
13 49 63 68
0 49 32 42
13 27 32 23
4 27 63 42
13 49 19 42
(⍋x) ≡ ⍋ ordinal x
1
Suppose x
is to be sorted ascending in columns 0 and 2 and descending in columns 1 and 3. The control array is 1 ¯1 1 ¯1
, and:
x 1 ¯1 1 ¯1 ado x
┌─┬─────┬─┬───────┐ ┌─┬─────┬─┬───────┐
│2│beta │b│baker │ │0│beta │a│able │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│3│alpha│a│able │ │0│beta │b│charlie│
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│3│beta │b│able │ │0│beta │b│baker │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│3│alpha│b│baker │ │0│beta │c│charlie│
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│1│beta │b│charlie│ │0│beta │c│able │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│1│beta │a│baker │ │0│alpha│c│baker │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│0│beta │c│charlie│ │1│beta │a│baker │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│0│beta │b│baker │ │1│beta │b│charlie│
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│0│beta │c│able │ │1│alpha│c│able │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│0│beta │a│able │ │2│beta │a│baker │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│3│alpha│a│baker │ │2│beta │b│baker │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│3│alpha│a│baker │ │3│beta │b│able │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│1│alpha│c│able │ │3│beta │b│able │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│0│beta │b│charlie│ │3│beta │c│charlie│
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│0│alpha│c│baker │ │3│alpha│a│baker │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│3│beta │b│able │ │3│alpha│a│baker │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│2│beta │a│baker │ │3│alpha│a│able │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│3│beta │c│charlie│ │3│alpha│a│able │
├─┼─────┼─┼───────┤ ├─┼─────┼─┼───────┤
│3│alpha│a│able │ │3│alpha│b│baker │
└─┴─────┴─┴───────┘ └─┴─────┴─┴───────┘
Finally
(ordinal x) ≡ ordinal ordinal x
1
That is, ordinal
is idempotent. Actually, this is kind of obvious, but I never miss an opportunity to use the word “idempotent”.☺