Fifty Shades of J/Chapter 22
Table of Contents ... Glossary ... Previous Chapter ... Next Chapter
Principal Topics
- { (from) {. (take) , (append) ,. (stitch) ,: (laminate) ; (link) /: (grade up) # (tally, copy) >: (increment) “: (rank conjunction) “: (format) >. (larger of) merging lists, row and column headings, row and column proportions
The Index List
Every time a list is created, several other lists are automatically created whether the user realises it or not. The most obvious is the index-list i.@# . Next are the grade up and grade down lists which are permutations of the index-list which give the indices necessary to arrange the original list in either ascending or descending order. Go one step further and obtain the index-list of the result of appending two lists, and you have the basis for merging lists as the next section shows.
Merging lists with alternate items
1 2 3 4 merged with 5 6 is to become 1 5 2 6 3 4. This illustrates well the concept of fork.
malt=.mselect { ,
that is start by joining the two lists as they stand (1 2 3 4 5 6), and then find the right selection list in order to put the items of the joined list in the required order. A moment’s thought shows that the required selection list is going to be 0 0 1 1 2 2 …
Every list has an index list given by
ilist=.i.@# ilist 'abcde' 0 1 2 3 4
Joining the index lists of the lists to be merged is a first step along the road
ijoin=.,& ilist 1 2 3 ijoin 4 5 0 1 2 0 1
The positions of the items in this list in ascending order are provided by grade up, so
mselect=./:@ijoin 1 2 3 mselect 4 5 0 3 1 4 2
and so finally
1 2 3 4 malt 5 6 1 5 2 6 3 4
Other merge patterns are now easy to improvise, for example if two from the left are to be taken for every one from the right adjust two of the verbs
ijoin21=.dyad : '(2#ilist x),(ilist y)' mselect21=./:@ijoin21 malt21=.dyad : '(x mselect21 y){ (2#x),y' 1 2 3 malt21 7 8 1 1 7 2 2 8 3 3
The remaining sections deal with joining lists to tables.
Row and Column Headings
Readers will be familiar with automatically adjusted headings generated in Excel and other spreadsheets. The algorithms for doing this can be conveniently analysed in J, and illustrate when it is appropriate to join and when to stitch when working with lists of lists. Start with some random data, a list or row headings and a list of column headings
]a=.?3 4$100 90 47 58 29 22 32 55 5 55 73 58 50 rh=.'London';'Paris';'Dublin' ch=.'North';'South';'East';'West'
The items in a can be boxed in a neat turn of phrase :
<&>a NB. otherwise <each a ┌──┬──┬──┬──┐ │90│47│58│29│ ├──┼──┼──┼──┤ │22│32│55│5 │ ├──┼──┼──┼──┤ │55│73│58│50│ └──┴──┴──┴──┘
However to merge column headings with the data it is necessary to do some counting of character spaces as well as determining how many decimal places are to be displayed in the data. Assume a field width of 6 and 1 decimal place, and column headers can be dealt with by
]b=.6j1":each a ┌──────┬──────┬──────┬──────┐ │ 90.0│ 47.0│ 58.0│ 29.0│ ├──────┼──────┼──────┼──────┤ │ 22.0│ 32.0│ 55.0│ 5.0│ ├──────┼──────┼──────┼──────┤ │ 55.0│ 73.0│ 58.0│ 50.0│ └──────┴──────┴──────┴──────┘ Ch=._6{.each ch Ch,b NB. Column headers are merged ┌──────┬──────┬──────┬──────┐ │ North│ South│ East│ West│ ├──────┼──────┼──────┼──────┤ │ 90.0│ 47.0│ 58.0│ 29.0│ ├──────┼──────┼──────┼──────┤ │ 22.0│ 32.0│ 55.0│ 5.0│ ├──────┼──────┼──────┼──────┤ │ 55.0│ 73.0│ 58.0│ 50.0│ └──────┴──────┴──────┴──────┘
The row headers also require some enhancement to deal with the space in the North-west corner :
Rh=.' ';rh Rh,.Ch,b NB. Row headers are stitched (,.) ┌──────┬──────┬──────┬──────┬──────┐ │ │ North│ South│ East│ West│ ├──────┼──────┼──────┼──────┼──────┤ │London│ 90.0│ 47.0│ 58.0│ 29.0│ ├──────┼──────┼──────┼──────┼──────┤ │Paris │ 22.0│ 32.0│ 55.0│ 5.0│ ├──────┼──────┼──────┼──────┼──────┤ │Dublin│ 55.0│ 73.0│ 58.0│ 50.0│ └──────┴──────┴──────┴──────┴──────┘
Mission accomplished! A generalization of this process is to compute the maximum width required for a single column header (assuming that this will be no greater than the required header width), allowing for a space (>:), and use this to determine the file width in the data,
eqlise=.monad : '->:>./>#every y' Ch=.(eqlise ch){.every ch b=.<&>((-eqlise ch)j.1)":each a
Rh,.Ch,b then gives the same result as above.
Some variations might be to eliminate the vertical grid lines within the data :
Rh,.<"1(,>Ch),6j1":a ┌──────┬────────────────────────┐ │ │ North South East West│ ├──────┼────────────────────────┤ │London│ 90.0 47.0 58.0 29.0│ ├──────┼────────────────────────┤ │Paris │ 22.0 32.0 55.0 5.0│ ├──────┼────────────────────────┤ │Dublin│ 55.0 73.0 58.0 50.0│ └──────┴────────────────────────┘
Alternatively for a display without grid lines
(>Rh),.(,>Ch),6j1":a North South East West London 90.0 47.0 58.0 29.0 Paris 22.0 32.0 55.0 5.0 Dublin 55.0 73.0 58.0 50.0
In summary to decorate a basic table a with grids and row and column headers requires equalisation for the column headers and augmentation for the row headers leading to the definition of Rh,.Ch,b .
Row and Column Totals
Appending row and column totals to a table :
a=.2 3$1 7 4 5 2 0 colsum=., +/ rowsum=.,. +/"1 colsum a 1 7 4 5 2 0 6 9 4 rowsum a 1 7 4 12 5 2 0 7 totsum a 1 7 4 12 5 2 0 7 6 9 4 19
To dress this with nice grid lines, proceed as follows :
f=.5j1&": NB. decide width and dec pl Rs=.f,.+/"1 a NB. formatted rows Cs=.f+/a NB. formatted columns Ts=.f+/,a NB. formatted total ((<f a),.<Rs),:<every Cs;Ts ┌───────────────┬─────┐ │ 1.0 7.0 5.0│ 13.0│ │ 5.0 2.0 0.0│ 7.0│ ├───────────────┼─────┤ │ 6.0 9.0 5.0│ 20.0│ └───────────────┴─────┘
all of which can be brought together in a user-defined verb :
ttable=.dyad :0 f=.x&": NB. x is (width)j(dec places) Rs=.f,.+/"1 y NB. formatted rows Cs=.f+/y NB. formatted columns Ts=.f+/,y NB. formatted total ((<f y),.<Rs),:<every Cs;Ts ) 5j1 ttable a ┌───────────────┬─────┐ │ 1.0 7.0 5.0│ 13.0│ │ 5.0 2.0 0.0│ 7.0│ ├───────────────┼─────┤ │ 6.0 9.0 5.0│ 20.0│ └───────────────┴─────┘
If row and column proportions are wanted :
colpro=.%"1 +/ rowpro=.% +/"1 colpro a 0.166667 0.777778 1 0.833333 0.222222 0 rowpro a 0.0833333 0.583333 0.333333 0.714286 0.285714 0
which might be clearer in rational (extended precision) notation :
b=.x: a colpro b 1r6 7r9 1 5r6 2r9 0 rowpro b 1r12 7r12 1r3 5r7 2r7 0
Code Summary
malt=: mselect { , NB. merge alternate mselect=: /:@ijoin ijoin=: ,& ilist ilist=: i.@# malt21=: dyad : '(x mselect21 y){ (2#x),y' NB. 2/1 merge mselect21=: /:@ijoin21 ijoin21=: dyad : '(2#ilist x),(ilist y)' eqlise=: monad : '->:>./>#every y' NB. equalise column widths colsum=: , +/ rowsum=: ,. +/"1 totsum=: colsum@rowsum NB. Adding row and column totals ttable=: dyad :0 NB.table with row & col totals f=. x&": NB. x is (width)j(dec places) Rs=. f,.+/"1 y NB. formatted rows Cs=. f+/y NB. formatted columns Ts=. f+/,y NB. formatted total ((<f y),.<Rs),:<every Cs;Ts ) NB. Row and column proportions colpro=: %"1 +/ NB. column proportions rowpro=: % +/"1 NB. row proportions