tdb - a text database

tdb is an in-memory non-relational "Text DataBase" (tdb) and reporting language that uses N-dimensional AVL trees. to approximate a sparse, N-dimensional hyper-cube.

Here are some trivial examples to illustrate a few of the most basic tdb features and capabilities.

In particular, note how the foreach and sort statements are used. Full details can be found in the tdb Users' Guide.

Download tdb from github.

tdb Usage Message

$ tdb usage (v0.1.2): tdb -f field_file -s select_file [OPTIONS] data_file [...] or tdb -f field_file -r report_file [OPTIONS] data_file [...] where OPTIONS are any of: -v : verbose/statistics mode -d delimiter_character : data file field delimiter character -1 : skip line #1 of delimited data file(s) -q quote_character (default ") : quote character for delimited data file(s) -o output_file : output file -I variable_name=integer_value : define an integer cmd line variable -F variable_name=floating_point_value : define a float cmd line variable -S variable_name=string_value : define a string cmd line variable Notes: Use `-' to specify stdin as a `data_file' Define variables like this: -I foobar=42 (ie, no spaces around the `=') tdb Copyright (C) 1991-2016 James S. Crook This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. This program is licensed under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version (see <http://www.gnu.org/licenses/>).

Expenditure Fields File (exp.f)

string category int date string penguin float amount string description

Expenditure Data File (exp.d)

"Air Fare" 20151201 Tux 3000.00 "Vladivostok" "Air Fare" 20151203 Tux 2500.00 "Sydney" "Air Fare" 20151206 Tux 3500.00 "Hong Kong" # "Coffee" 20151201 Tux 3.50 "Carpe Diem" "Coffee" 20151202 Tux 3.50 "Carpe Diem" "Coffee" 20151203 Tux 3.50 "Carpe Diem" "Coffee" 20151204 Tux 3.50 "Carpe Diem" "Coffee" 20151205 Tux 3.50 "Carpe Diem" "Coffee" 20151206 Tux 3.50 "Carpe Diem" "Coffee" 20151207 Tux 3.50 "Carpe Diem" "Coffee" 20151203 Tux 3.50 "Carpe Diem" "Coffee" 20151205 Tux 3.50 "Carpe Diem" # "Fuel" 20151203 Tux 70.00 "Lamborghini" "Fuel" 20151205 Tux 80.00 "Lamborghini" "Fuel" 20151207 Tux 90.00 "Lamborghini" "Fuel" 20151202 Tux 70.00 "Maserati" "Fuel" 20151204 Tux 80.00 "Maserati" "Fuel" 20151206 Tux 90.00 "Maserati" # "Train Ticket" 20151202 Tux 10.50 "Office" "Train Ticket" 20151204 Tux 10.50 "Office" "Train Ticket" 20151206 Tux 10.50 "Office" "Train Ticket" 20151203 Tux 19.20 "Football game" #################################################################### "Entertainment" 20151203 Tux 81.00 "Wine" "Entertainment" 20151204 Tux 83.00 "Wine" "Entertainment" 20151205 Tux 85.00 "Wine" "Entertainment" 20151206 Tux 57.00 "Wine" "Entertainment" 20151207 Tux 89.00 "Wine" "Entertainment" 20151203 Tux 41.00 "Beer" "Entertainment" 20151204 Tux 43.00 "Beer" "Entertainment" 20151205 Tux 45.00 "Beer" "Entertainment" 20151206 Tux 47.00 "Beer" "Entertainment" 20151207 Tux 49.00 "Beer" "Entertainment" 20151204 Tux 13.00 "Movie" #################################################################### "Coffee" 20151201 Tuxette 3.50 "Morning Java" "Coffee" 20151202 Tuxette 3.50 "Morning Java" "Coffee" 20151203 Tuxette 3.50 "Morning Java" "Coffee" 20151204 Tuxette 3.50 "Morning Java" "Coffee" 20151205 Tuxette 3.50 "Morning Java" "Coffee" 20151206 Tuxette 3.50 "Morning Java" "Coffee" 20151207 Tuxette 3.50 "Morning Java" "Coffee" 20151203 Tuxette 3.50 "Morning Java" "Coffee" 20151205 Tuxette 3.50 "Morning Java" "Entertainment" 20151201 Tuxette 60.50 "Cocktails" "Entertainment" 20151202 Tuxette 85.50 "Wine" "Entertainment" 20151206 Tuxette 62.00 "Cocktails" "Entertainment" 20151206 Tuxette 190.50 "Restaurant" "Bills" 20151202 Tuxette 400.50 "Electricity" "Bills" 20151203 Tuxette 125.50 "Gas" "Bills" 20151203 Tuxette 120.50 "Water" "Bills" 20151206 Tuxette 250.50 "Garbage" "Clothing" 20151201 Tuxette 350.50 "Leather Jacket" "Clothing" 20151206 Tuxette 270.50 "Lingerie" "Groceries" 20151206 Tuxette 389.50 "Weekly shop" "Groceries" 20151206 Tuxette 49.50 "Liquor" "Air Fare" 20151203 Tuxette 530.00 "Las Vegas" "Entertainment" 20151204 Tuxette 13.00 "Movie" #################################################################### "Food" 20151201 Tuxina 4.50 "School lunch" "Food" 20151202 Tuxina 4.50 "School lunch" "Food" 20151203 Tuxina 4.50 "School lunch" "Food" 20151204 Tuxina 4.50 "School lunch" "Food" 20151205 Tuxina 4.50 "School lunch" "Supplies" 20151201 Tuxina 8.50 "Pencils" "Supplies" 20151202 Tuxina 85.50 "Books" "Fun" 20151206 Tuxina 3.50 "Swimming pool" "Entertainment" 20151204 Tuxina 13.00 "Movie" "Train Ticket" 20151208 Tuxina 9.30 "School" "Train Ticket" 20151209 Tuxina 9.30 "School" "Train Ticket" 20151210 Tuxina 9.30 "School" "Train Ticket" 20151211 Tuxina 9.30 "School" "Train Ticket" 20151212 Tuxina 9.30 "School" #################################################################### "Supplies" 20151201 "Tux Jr." 82.50 "Books" "Supplies" 20151203 "Tux Jr." 850.50 "Laptop" "Supplies" 20151205 "Tux Jr." 208.50 "SSD drive" "Supplies" 20151205 "Tux Jr." 10.50 "Crayons" "Food" 20151201 "Tux Jr." 4.50 "School lunch" "Food" 20151202 "Tux Jr." 4.50 "School lunch" "Food" 20151203 "Tux Jr." 4.50 "School lunch" "Food" 20151204 "Tux Jr." 4.50 "School lunch" "Food" 20151205 "Tux Jr." 4.50 "School lunch" "Food" 20151203 "Tux Jr." 3.50 "School snack" "Fun" 20151207 "Tux Jr." 33.40 "Ice sailing" "Entertainment" 20151204 "Tux Jr." 13.00 "Movie" "Train Ticket" 20151208 "Tux Jr." 9.30 "School" "Train Ticket" 20151209 "Tux Jr." 9.30 "School" "Train Ticket" 20151210 "Tux Jr." 9.30 "School" "Train Ticket" 20151211 "Tux Jr." 9.30 "School" "Train Ticket" 20151212 "Tux Jr." 9.30 "School" #################################################################### "Entertainment" 20151206 Tux 1444.44 "Beer" "Entertainment" 20151206 Tux 3450.00 "Booze" "Entertainment" 20151206 Tux 1235.00 "Cigars" "Entertainment" 20151206 Tux 850.00 "Cognac" "Entertainment" 20151206 Tux 3500.00 "Cocaine" "Entertainment" 20151206 Tux 1900.00 "Strippers" "Entertainment" 20151206 Tux 2400.00 "Hookers" "Entertainment" 20151206 Tux 7531.50 "Restaurant" "Entertainment" 20151206 Tux 2468.42 "Can't remember" "Firearms" 20151206 Tux 1500.00 "Glock 19" "Firearms" 20151207 Tux 9500.00 "RPG Launcher" "Firearms" 20151209 Tux 2550.00 "Ammo" #################################################################### "Coffee" 20151208 Tux 3.50 "Carpe Diem" "Coffee" 20151209 Tux 3.50 "Carpe Diem" "Coffee" 20151210 Tux 3.50 "Carpe Diem" "Coffee" 20151211 Tux 3.50 "Carpe Diem" "Coffee" 20151212 Tux 3.50 "Carpe Diem" "Coffee" 20151213 Tux 3.50 "Carpe Diem" "Coffee" 20151214 Tux 3.50 "Carpe Diem"

Expenditure by Category and Penguin - a Trivial Report Program

aggregate(category, penguin); foreach(category:) { printf("%-15s", category); foreach(penguin:) printf("%10.2f", sum(amount: category, penguin)); printf("\n"); }

Running tdb in report mode:

$ tdb -f exp.f -r exp_cat_penguin_basic.r exp.d Air Fare 9000.00 0.00 530.00 0.00 Bills 0.00 0.00 897.00 0.00 Clothing 0.00 0.00 621.00 0.00 Coffee 56.00 0.00 31.50 0.00 Entertainment 25412.36 13.00 411.50 13.00 Firearms 13550.00 0.00 0.00 0.00 Food 0.00 26.00 0.00 22.50 Fuel 480.00 0.00 0.00 0.00 Fun 0.00 33.40 0.00 3.50 Groceries 0.00 0.00 439.00 0.00 Supplies 0.00 1152.00 0.00 94.00 Train Ticket 50.70 46.50 0.00 46.50

This report output consists of rows of expenditure by category with a column for each penguin. It would be much more useful if it had a title, column headers, totals by category, totals by penguin and if the categories were further broken down by expenditure description. For example...

Expenditure by Category, Description and Penguin

aggregate(penguin, category, description); printf("Tux Family Expenditure Report by Penguin: %s\n\n", formatdate("%d-%b-%y", reportdate())); sort(category: sum(amount: category)); sort(penguin: sum(amount: penguin)); foreach(category:) { sort(description: sum(amount: category, description)); printf("%-15s%8s", category, "Count"); foreach(penguin:) { printf("%10s", penguin); } printf("%10s (%d) items\n", "Total", number(description: category)); foreach(description: category) { printf("%15s%8d", description, count(category, description)); foreach(penguin:) { printf("%10.2f", sum(amount: category, description, penguin)); } printf("%10.2f\n", sum(amount: category, description)); } printf("%-15s%8d", "Subtotal", count(category)); foreach(penguin:) { printf("%10.2f", sum(amount: category, penguin)); } printf("%10.2f\n\n", sum(amount: category)); } printf("%-15s%8d", "Total", count()); foreach(penguin:) { printf("%10.2f", sum(amount: penguin)); } printf("%10.2f\n", sum(amount:));

Note the use of sum, count and number to perform the calculations - without using any variables! Also, the categories are sorted by the total category spend (ascending), the descriptions within each category are sorted by those totals (ascending), and the penguins are sorted by their total spend (ascending).

$ tdb -f exp.f -r exp_cat_descr_penguin.r exp.d Tux Family Expenditure Report by Penguin: 19-Jun-22 Fun Count Tuxina Tux Jr. Tuxette Tux Total (2) items Swimming pool 1 3.50 0.00 0.00 0.00 3.50 Ice sailing 1 0.00 33.40 0.00 0.00 33.40 Subtotal 2 3.50 33.40 0.00 0.00 36.90 Food Count Tuxina Tux Jr. Tuxette Tux Total (2) items School snack 1 0.00 3.50 0.00 0.00 3.50 School lunch 10 22.50 22.50 0.00 0.00 45.00 Subtotal 11 22.50 26.00 0.00 0.00 48.50 Coffee Count Tuxina Tux Jr. Tuxette Tux Total (2) items Morning Java 9 0.00 0.00 31.50 0.00 31.50 Carpe Diem 16 0.00 0.00 0.00 56.00 56.00 Subtotal 25 0.00 0.00 31.50 56.00 87.50 Train Ticket Count Tuxina Tux Jr. Tuxette Tux Total (3) items Football game 1 0.00 0.00 0.00 19.20 19.20 Office 3 0.00 0.00 0.00 31.50 31.50 School 10 46.50 46.50 0.00 0.00 93.00 Subtotal 14 46.50 46.50 0.00 50.70 143.70 Groceries Count Tuxina Tux Jr. Tuxette Tux Total (2) items Liquor 1 0.00 0.00 49.50 0.00 49.50 Weekly shop 1 0.00 0.00 389.50 0.00 389.50 Subtotal 2 0.00 0.00 439.00 0.00 439.00 Fuel Count Tuxina Tux Jr. Tuxette Tux Total (2) items Lamborghini 3 0.00 0.00 0.00 240.00 240.00 Maserati 3 0.00 0.00 0.00 240.00 240.00 Subtotal 6 0.00 0.00 0.00 480.00 480.00 Clothing Count Tuxina Tux Jr. Tuxette Tux Total (2) items Lingerie 1 0.00 0.00 270.50 0.00 270.50 Leather Jacket 1 0.00 0.00 350.50 0.00 350.50 Subtotal 2 0.00 0.00 621.00 0.00 621.00 Bills Count Tuxina Tux Jr. Tuxette Tux Total (4) items Water 1 0.00 0.00 120.50 0.00 120.50 Gas 1 0.00 0.00 125.50 0.00 125.50 Garbage 1 0.00 0.00 250.50 0.00 250.50 Electricity 1 0.00 0.00 400.50 0.00 400.50 Subtotal 4 0.00 0.00 897.00 0.00 897.00 Supplies Count Tuxina Tux Jr. Tuxette Tux Total (5) items Pencils 1 8.50 0.00 0.00 0.00 8.50 Crayons 1 0.00 10.50 0.00 0.00 10.50 Books 2 85.50 82.50 0.00 0.00 168.00 SSD drive 1 0.00 208.50 0.00 0.00 208.50 Laptop 1 0.00 850.50 0.00 0.00 850.50 Subtotal 6 94.00 1152.00 0.00 0.00 1246.00 Air Fare Count Tuxina Tux Jr. Tuxette Tux Total (4) items Las Vegas 1 0.00 0.00 530.00 0.00 530.00 Sydney 1 0.00 0.00 0.00 2500.00 2500.00 Vladivostok 1 0.00 0.00 0.00 3000.00 3000.00 Hong Kong 1 0.00 0.00 0.00 3500.00 3500.00 Subtotal 4 0.00 0.00 530.00 9000.00 9530.00 Firearms Count Tuxina Tux Jr. Tuxette Tux Total (3) items Glock 19 1 0.00 0.00 0.00 1500.00 1500.00 Ammo 1 0.00 0.00 0.00 2550.00 2550.00 RPG Launcher 1 0.00 0.00 0.00 9500.00 9500.00 Subtotal 3 0.00 0.00 0.00 13550.00 13550.00 Entertainment Count Tuxina Tux Jr. Tuxette Tux Total (12) items Movie 4 13.00 13.00 13.00 13.00 52.00 Cocktails 2 0.00 0.00 122.50 0.00 122.50 Wine 6 0.00 0.00 85.50 395.00 480.50 Cognac 1 0.00 0.00 0.00 850.00 850.00 Cigars 1 0.00 0.00 0.00 1235.00 1235.00 Beer 6 0.00 0.00 0.00 1669.44 1669.44 Strippers 1 0.00 0.00 0.00 1900.00 1900.00 Hookers 1 0.00 0.00 0.00 2400.00 2400.00 Can't remember 1 0.00 0.00 0.00 2468.42 2468.42 Booze 1 0.00 0.00 0.00 3450.00 3450.00 Cocaine 1 0.00 0.00 0.00 3500.00 3500.00 Restaurant 2 0.00 0.00 190.50 7531.50 7722.00 Subtotal 27 13.00 13.00 411.50 25412.36 25849.86 Total 106 179.50 1270.90 2930.00 48549.06 52929.46

Expenditure by Category and Date

In this report, sort is used to order the sum of expenditure by category, decreasing.
aggregate(category, date); function displaylines() { printf("---------------"); foreach(date:) { printf(" ---------"); } printf(" ---------\n", "Total"); } printf("Tux Family Expenditure Report - %s\n\n", formatdate("%d-%b-%y", reportdate())); displaylines(); printf("%-15s", "Category"); foreach(date:) { printf("%10s", formatdate("%d-%b-%y", date)); } printf("%10s\n", "Total"); displaylines(); sort(category: -sum(amount: category)); foreach(category:) { printf("%-15s", category); foreach(date:) { cat_date_amt = sum(amount: category, date); if (cat_date_amt != 0) { printf("%10.2f", cat_date_amt); } else { printf("%10s", ""); } } printf("%10.2f\n", sum(amount: category)); } displaylines(); printf("%-15s", "Total"); foreach(date:) { printf("%10.2f", sum(amount: date)); } printf("%10.2f\n", sum(amount:)); displaylines();

$ tdb -f exp.f -r exp_cat_date.r exp.d Tux Family Expenditure Report - 19-Jun-22 --------------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 08-Dec-15 09-Dec-15 10-Dec-15 11-Dec-15 12-Dec-15 13-Dec-15 14-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- Entertainment 60.50 85.50 122.00 178.00 130.00 25135.86 138.00 25849.86 Firearms 1500.00 9500.00 2550.00 13550.00 Air Fare 3000.00 3030.00 3500.00 9530.00 Supplies 91.00 85.50 850.50 219.00 1246.00 Bills 400.50 246.00 250.50 897.00 Clothing 350.50 270.50 621.00 Fuel 70.00 70.00 80.00 80.00 90.00 90.00 480.00 Groceries 439.00 439.00 Train Ticket 10.50 19.20 10.50 10.50 18.60 18.60 18.60 18.60 18.60 143.70 Coffee 7.00 7.00 14.00 7.00 14.00 7.00 7.00 3.50 3.50 3.50 3.50 3.50 3.50 3.50 87.50 Food 9.00 9.00 12.50 9.00 9.00 48.50 Fun 3.50 33.40 36.90 --------------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- Total 3518.00 668.00 4364.20 284.50 452.00 31206.86 9768.40 22.10 2572.10 22.10 22.10 22.10 3.50 3.50 52929.46 --------------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- ---------

Note the totals are sorted descending. This report output has a column for every date. Obviously, it would be too wide for normal data (with lots of dates). To address this issue, tdb in select mode can be used to select only the data of interest.

Expenditure Select File (exp_1_week.s)

select(date >= 20151201 && date <= 20151207);

This is the same report as above, but this time tdb is called twice - first in select mode (to choose only the required data - 1 week's worth in this example) and that data is piped to a second tdb instance running in report mode:

$ tdb -f exp.f -s exp_1_week.s exp.d | tdb -f exp.f -r exp_cat_date.r - Tux Family Expenditure Report - 19-Jun-22 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- Entertainment 60.50 85.50 122.00 178.00 130.00 25135.86 138.00 25849.86 Firearms 1500.00 9500.00 11000.00 Air Fare 3000.00 3030.00 3500.00 9530.00 Supplies 91.00 85.50 850.50 219.00 1246.00 Bills 400.50 246.00 250.50 897.00 Clothing 350.50 270.50 621.00 Fuel 70.00 70.00 80.00 80.00 90.00 90.00 480.00 Groceries 439.00 439.00 Coffee 7.00 7.00 14.00 7.00 14.00 7.00 7.00 63.00 Train Ticket 10.50 19.20 10.50 10.50 50.70 Food 9.00 9.00 12.50 9.00 9.00 48.50 Fun 3.50 33.40 36.90 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Total 3518.00 668.00 4364.20 284.50 452.00 31206.86 9768.40 50261.96 --------------- --------- --------- --------- --------- --------- --------- --------- ---------

Again, the category expenditure total column is sorted in descending order. Also note that in the report above (with all the data), "Train Ticket" was before "Coffee" - but they have swapped position in this report (because the category totals are different).

Expenditure by Category, Description and Date

This report sorts by the sum of the expenditure based on the descriptions field, ascending - within each category.

aggregate(category, date, description); function displaylines() { printf("---------------"); foreach(date:) { printf(" ---------"); } printf(" ---------\n", "Total"); } printf("Tux Family Itemized Expenditure Report - %s\n\n", formatdate("%d-%b-%y", reportdate())); displaylines(); printf("%-15s", "Category"); foreach(date:) { printf("%10s", formatdate("%d-%b-%y", date)); } printf("%10s\n", "Total"); displaylines(); foreach(category:) { printf("%-15s\n", category); sort(description: sum(amount: category, description)); foreach(description: category) { printf("%15s", description); foreach(date:) { amt = sum(amount: category, date, description); if (amt != 0) { printf("%10.2f", amt); } else { printf("%10s", ""); } } printf("%10.2f\n", sum(amount: category, description)); } displaylines(); printf("%15s", "Subtotal"); foreach(date:) { amt = sum(amount: category, date); if (amt != 0) { printf("%10.2f", amt); } else { printf("%10s", ""); } } printf("%10.2f\n", sum(amount: category)); displaylines(); printf("\n"); } displaylines(); printf("%-15s", "Total"); foreach(date:) { printf("%10.2f", sum(amount: date)); } printf("%10.2f\n", sum(amount:)); displaylines();

$ tdb -f exp.f -s exp_1_week.s exp.d | tdb -f exp.f -r exp_cat_descr_date.r - Tux Family Itemized Expenditure Report - 19-Jun-22 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- Air Fare Las Vegas 530.00 530.00 Sydney 2500.00 2500.00 Vladivostok 3000.00 3000.00 Hong Kong 3500.00 3500.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 3000.00 3030.00 3500.00 9530.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Bills Water 120.50 120.50 Gas 125.50 125.50 Garbage 250.50 250.50 Electricity 400.50 400.50 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 400.50 246.00 250.50 897.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Clothing Lingerie 270.50 270.50 Leather Jacket 350.50 350.50 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 350.50 270.50 621.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Coffee Carpe Diem 3.50 3.50 7.00 3.50 7.00 3.50 3.50 31.50 Morning Java 3.50 3.50 7.00 3.50 7.00 3.50 3.50 31.50 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 7.00 7.00 14.00 7.00 14.00 7.00 7.00 63.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Entertainment Movie 52.00 52.00 Cocktails 60.50 62.00 122.50 Wine 85.50 81.00 83.00 85.00 57.00 89.00 480.50 Cognac 850.00 850.00 Cigars 1235.00 1235.00 Beer 41.00 43.00 45.00 1491.44 49.00 1669.44 Strippers 1900.00 1900.00 Hookers 2400.00 2400.00 Can't remember 2468.42 2468.42 Booze 3450.00 3450.00 Cocaine 3500.00 3500.00 Restaurant 7722.00 7722.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 60.50 85.50 122.00 178.00 130.00 25135.86 138.00 25849.86 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Firearms Glock 19 1500.00 1500.00 RPG Launcher 9500.00 9500.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 1500.00 9500.00 11000.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Food School snack 3.50 3.50 School lunch 9.00 9.00 9.00 9.00 9.00 45.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 9.00 9.00 12.50 9.00 9.00 48.50 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Fuel Lamborghini 70.00 80.00 90.00 240.00 Maserati 70.00 80.00 90.00 240.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 70.00 70.00 80.00 80.00 90.00 90.00 480.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Fun Swimming pool 3.50 3.50 Ice sailing 33.40 33.40 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 3.50 33.40 36.90 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Groceries Liquor 49.50 49.50 Weekly shop 389.50 389.50 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 439.00 439.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Supplies Pencils 8.50 8.50 Crayons 10.50 10.50 Books 82.50 85.50 168.00 SSD drive 208.50 208.50 Laptop 850.50 850.50 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 91.00 85.50 850.50 219.00 1246.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Train Ticket Football game 19.20 19.20 Office 10.50 10.50 10.50 31.50 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Subtotal 10.50 19.20 10.50 10.50 50.70 --------------- --------- --------- --------- --------- --------- --------- --------- --------- --------------- --------- --------- --------- --------- --------- --------- --------- --------- Total 3518.00 668.00 4364.20 284.50 452.00 31206.86 9768.40 50261.96 --------------- --------- --------- --------- --------- --------- --------- --------- ---------

Expenditure by Penguin, Category and Date (active dates)

By default (when sort is not used), every field's data is sorted ascending in alphabetical (for string fields) or numerical (for int and float fields) order.

aggregate(penguin, category, date); function displaylines() { printf("---------------"); foreach(date: penguin) { printf(" ---------"); } printf(" ---------\n", "Total"); } printf("Tux Family Expenditure By Category Report - %s\n\n", formatdate("%d-%b-%y", reportdate())); foreach(penguin:) { printf("\n============== Expenditure for %s ===============\n", penguin); displaylines(); printf("%-15s", "Category"); foreach(date: penguin) { printf("%10s", formatdate("%d-%b-%y", date)); } printf("%10s\n", "Total"); displaylines(); foreach(category: penguin) { printf("%-15s", category); foreach(date: penguin) { cat_date_amt = sum(amount: penguin, category, date); if (cat_date_amt != 0) { printf("%10.2f", cat_date_amt); } else { printf("%10s", ""); } } printf("%10.2f\n", sum(amount: penguin, category)); } displaylines(); printf("%-15s", "Total"); foreach(date: penguin) { printf("%10.2f", sum(amount: penguin, date)); } printf("%10.2f\n", sum(amount: penguin)); displaylines(); }

$ tdb -f exp.f -s exp_1_week.s exp.d | tdb -f exp.f -r exp_penguin_cat_act_dates.r - Tux Family Expenditure By Category Report - 19-Jun-22 ============== Expenditure for Tux =============== --------------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- Air Fare 3000.00 2500.00 3500.00 9000.00 Coffee 3.50 3.50 7.00 3.50 7.00 3.50 3.50 31.50 Entertainment 122.00 139.00 130.00 24883.36 138.00 25412.36 Firearms 1500.00 9500.00 11000.00 Fuel 70.00 70.00 80.00 80.00 90.00 90.00 480.00 Train Ticket 10.50 19.20 10.50 10.50 50.70 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Total 3003.50 84.00 2718.20 233.00 217.00 29987.36 9731.50 45974.56 --------------- --------- --------- --------- --------- --------- --------- --------- --------- ============== Expenditure for Tux Jr. =============== --------------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- Entertainment 13.00 13.00 Food 4.50 4.50 8.00 4.50 4.50 26.00 Fun 33.40 33.40 Supplies 82.50 850.50 219.00 1152.00 --------------- --------- --------- --------- --------- --------- --------- --------- Total 87.00 4.50 858.50 17.50 223.50 33.40 1224.40 --------------- --------- --------- --------- --------- --------- --------- --------- ============== Expenditure for Tuxette =============== --------------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- Air Fare 530.00 530.00 Bills 400.50 246.00 250.50 897.00 Clothing 350.50 270.50 621.00 Coffee 3.50 3.50 7.00 3.50 7.00 3.50 3.50 31.50 Entertainment 60.50 85.50 13.00 252.50 411.50 Groceries 439.00 439.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Total 414.50 489.50 783.00 16.50 7.00 1216.00 3.50 2930.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- ============== Expenditure for Tuxina =============== --------------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- Entertainment 13.00 13.00 Food 4.50 4.50 4.50 4.50 4.50 22.50 Fun 3.50 3.50 Supplies 8.50 85.50 94.00 --------------- --------- --------- --------- --------- --------- --------- --------- Total 13.00 90.00 4.50 17.50 4.50 3.50 133.00 --------------- --------- --------- --------- --------- --------- --------- ---------

Note that the date columns (in the output above) are not the same for all penguins. While this format might be useful in some cases, the report output might be more readable if all the dates were displayed for every penguin. This would require a trivial modification to the above program (see below).

Expenditure by Penguin, Category and Date - Report Source (all dates)

aggregate(penguin, category, date); function displaylines() { printf("---------------"); foreach(date:) { printf(" ---------"); } printf(" ---------\n", "Total"); } printf("Tux Family Expenditure By Penguin Report - %s\n\n", formatdate("%d-%b-%y", reportdate())); foreach(penguin:) { printf("\n============== Expenditure for %s ===============\n", penguin); displaylines(); printf("%-15s", "Category"); foreach(date:) { printf("%10s", formatdate("%d-%b-%y", date)); } printf("%10s\n", "Total"); displaylines(); foreach(category: penguin) { printf("%-15s", category); foreach(date:) { cat_date_amt = sum(amount: penguin, category, date); if (cat_date_amt != 0) { printf("%10.2f", cat_date_amt); } else { printf("%10s", ""); } } printf("%10.2f\n", sum(amount: penguin, category)); } displaylines(); printf("%-15s", "Total"); foreach(date:) { printf("%10.2f", sum(amount: penguin, date)); } printf("%10.2f\n", sum(amount: penguin)); displaylines(); }

$ tdb -f exp.f -s exp_1_week.s exp.d | tdb -f exp.f -r exp_penguin_cat_all_dates.r - Tux Family Expenditure By Penguin Report - 19-Jun-22 ============== Expenditure for Tux =============== --------------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- Air Fare 3000.00 2500.00 3500.00 9000.00 Coffee 3.50 3.50 7.00 3.50 7.00 3.50 3.50 31.50 Entertainment 122.00 139.00 130.00 24883.36 138.00 25412.36 Firearms 1500.00 9500.00 11000.00 Fuel 70.00 70.00 80.00 80.00 90.00 90.00 480.00 Train Ticket 10.50 19.20 10.50 10.50 50.70 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Total 3003.50 84.00 2718.20 233.00 217.00 29987.36 9731.50 45974.56 --------------- --------- --------- --------- --------- --------- --------- --------- --------- ============== Expenditure for Tux Jr. =============== --------------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- Entertainment 13.00 13.00 Food 4.50 4.50 8.00 4.50 4.50 26.00 Fun 33.40 33.40 Supplies 82.50 850.50 219.00 1152.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Total 87.00 4.50 858.50 17.50 223.50 0.00 33.40 1224.40 --------------- --------- --------- --------- --------- --------- --------- --------- --------- ============== Expenditure for Tuxette =============== --------------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- Air Fare 530.00 530.00 Bills 400.50 246.00 250.50 897.00 Clothing 350.50 270.50 621.00 Coffee 3.50 3.50 7.00 3.50 7.00 3.50 3.50 31.50 Entertainment 60.50 85.50 13.00 252.50 411.50 Groceries 439.00 439.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Total 414.50 489.50 783.00 16.50 7.00 1216.00 3.50 2930.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- ============== Expenditure for Tuxina =============== --------------- --------- --------- --------- --------- --------- --------- --------- --------- Category 01-Dec-15 02-Dec-15 03-Dec-15 04-Dec-15 05-Dec-15 06-Dec-15 07-Dec-15 Total --------------- --------- --------- --------- --------- --------- --------- --------- --------- Entertainment 13.00 13.00 Food 4.50 4.50 4.50 4.50 4.50 22.50 Fun 3.50 3.50 Supplies 8.50 85.50 94.00 --------------- --------- --------- --------- --------- --------- --------- --------- --------- Total 13.00 90.00 4.50 17.50 4.50 3.50 0.00 133.00 --------------- --------- --------- --------- --------- --------- --------- --------- ---------

Now all penguins have the same dates. Some "cells" are empty, but this output is more readable.

Some Other Very Useful Features - Grocery Store Prices

Grocery Fields File (gro.f)

string chain # Franklin's string location # Pennant Hills string item # beer coke bananas cereal float price # 8.00 48.00 3.95 3.55 int quantity # 6 24 1 1 float size # 0.375 1.125 1.0 500.0 string unit # ml ml kg g

Comments in the fields file.

Grocery Data File (gro.d)

Coles "Los Angeles" "Apples, Granny Smith" 5.50 1 1.0 kg Coles "Chicago" "Apples, Granny Smith" 4.00 1 1.0 kg Woolworths "Sydney" "Apples, Granny Smith" 6.00 1 1.0 kg Woolworths "Tokyo" "Apples, Granny Smith" 9.00 1 0.5 kg Coles "Sydney" "Bananas" 4.00 1 1.0 bag # sale price Coles "Sydney" "Bananas" 5.00 2 1.0 bag # regular price Woolworths "Tokyo" "Bananas" 9.00 3 0.5 bag Coles "Sydney" "Bananas" 5.00 1 1.0 kg Coles "Tokyo" "Bananas" 6.00 1 1.0 kg Woolworths "Los Angeles" "Bananas" 4.00 1 1.0 kg Woolworths "Chicago" "Bananas" 4.00 1 1.0 kg "Raju's Corner Shop" "London" "Bananas" 8.00 1 0.5 kg Coles "Sydney" "Beer, VB" 13.00 6 0.375 l # first time Coles "Sydney" "Beer, VB" 13.50 6 0.375 l # second time Coles "Sydney" "Beer, VB" 14.00 6 0.375 l # third time Coles "Adelaide" "Beer, VB" 15.75 6 0.375 l Woolworths "Sydney" "Beer, VB" 45.00 24 0.375 l Woolworths "Adelaide" "Beer, VB" 49.50 24 0.375 l Coles "Toronto" "Beer, Molson Golden" 32.74 24 0.341 l Woolworths "Toronto" "Beer, Molson Golden" 36.83 24 0.341 l Coles "Montreal" "Beer, Molson Golden" 40.92 24 0.341 l Woolworths "Montreal" "Beer, Molson Golden" 45.01 24 0.341 l

Note that there can also be comments in the data files, blank lines are ignored, and the same item has occasionally been purchased from the same "store".

The new field "store" is is created by concatenating the chain and location fields (see below).

Also, the data need not be aligned (as it is in these example data files); it must be separated by "white space".

Grocery Report Source

join(string store: chain, ", ", location); join(string item_unit: item, " ($/", unit, ")"); aggregate(store, item_unit, price, quantity, size); #include "gro.i" foreach(store:) tot_cost[store] = 0.0; /* must be a float */ foreach(item_unit:) { tot_price = 0.0; tot_spend = 0.0; unnecessary_array_a[item_unit] = 0; foreach(store: item_unit) { tot_store_price = 0.0; tot_store_spend = 0.0; foreach(price: item_unit, store) { foreach(quantity: item_unit, store, price) { foreach(size: item_unit, store, price, quantity) { c = count(size, quantity, price, item_unit, store); tot_store_price += c * price; tot_store_spend += c * quantity * size; } } } tot_cost[store] = tot_store_price/tot_store_spend; tot_price += tot_store_price; tot_spend += tot_store_spend; n = count(item_unit, store); unnecessary_array_a[item_unit] += n; unnecessary_multi_array_b[item_unit, store, "ridiculous", "indexes"] = n; } printf("%s\n", item_unit); sort(store: tot_cost[store]); foreach(store: item_unit) { printf(" %-33s%8.2f%9d\n", store, tot_cost[store], unnecessary_multi_array_b[item_unit, store, "ridiculous", "indexes"]); } printf(" %33s%8.2f%9d\n", "Weighted average", tot_price/tot_spend, unnecessary_array_a[item_unit]); printf("\n"); }

Using the join statement to effectively create new fields from the original ones can be particularly useful - as in this example.

This report also makes use of some other features that are (in this report) egregiously unnecessary. In particular, the use of the #include and multi-dimensional arrays could very easily have been avoided.

Grocery Report Include File (gro.i)

printf(" Avg Unit Number\n"); printf("Store Price Purch\n"); printf("============================== ======== ======\n");

Grocery Report Output

$ tdb -f gro.f -r gro.r gro.d Avg Unit Number Store Price Purch ============================== ======== ====== Apples, Granny Smith ($/kg) Coles, Chicago 4.00 1 Coles, Los Angeles 5.50 1 Woolworths, Sydney 6.00 1 Woolworths, Tokyo 18.00 1 Weighted average 7.00 4 Bananas ($/bag) Coles, Sydney 3.00 2 Woolworths, Tokyo 6.00 1 Weighted average 4.00 3 Bananas ($/kg) Woolworths, Chicago 4.00 1 Woolworths, Los Angeles 4.00 1 Coles, Sydney 5.00 1 Coles, Tokyo 6.00 1 Raju's Corner Shop, London 16.00 1 Weighted average 6.00 5 Beer, Molson Golden ($/l) Coles, Toronto 4.00 1 Woolworths, Toronto 4.50 1 Coles, Montreal 5.00 1 Woolworths, Montreal 5.50 1 Weighted average 4.75 4 Beer, VB ($/l) Woolworths, Sydney 5.00 1 Woolworths, Adelaide 5.50 1 Coles, Sydney 6.00 3 Coles, Adelaide 7.00 1 Weighted average 5.58 6

Please refer to the tdb Users' Guide for more info.

Based entirely on the particularly speculative and overwhelmingly circumstantial evidence above, Tux may have thrown an outraaaaaaaaaaaageous party in Hong Kong on 6 Dec! (But, as we know, what happens on the road stays on the road.)