Lua Project: Data Tool
A data tool is a program that reads rows of data, groups them, and prints a summary report. This tool writes its own small data file, then reads it back and aggregates it.
Writing then reading counts the rows
The program below writes two rows to a file, closes it, and reads it back. The row count proves the round trip worked.
local file = io.open("sales.csv", "w")
file:write("apple,10,2\n")
file:write("banana,5,3\n")
file:close()
local input = io.open("sales.csv", "r")
local rows = 0
for line in input:lines() do
rows = rows + 1
end
input:close()
print(rows)A pattern splits one row into fields
The pattern below captures the name and the two numbers from one row. Multiplying the two numbers gives that row revenue.
local line = "apple,10,2"
local name, qty, price = line:match("^(%w+),(%d+),(%d+)$")
print(name)
print(tonumber(qty) * tonumber(price))Reading rows builds totals
The program below writes three rows, reads them back, and adds up quantity and revenue. The totals prove the parsing and the adding.
local file = io.open("sales.csv", "w")
file:write("apple,10,2\n")
file:write("banana,5,3\n")
file:write("apple,4,2\n")
file:close()
local input = io.open("sales.csv", "r")
local rows = 0
local totalQty = 0
local totalRevenue = 0
for line in input:lines() do
local name, qty, price = line:match("^(%w+),(%d+),(%d+)$")
rows = rows + 1
totalQty = totalQty + tonumber(qty)
totalRevenue = totalRevenue + tonumber(qty) * tonumber(price)
end
input:close()
print(rows)
print(totalQty)
print(totalRevenue)Example
local file = io.open("sales.csv", "w")
file:write("apple,10,2\n")
file:write("banana,5,3\n")
file:write("apple,4,2\n")
file:write("cherry,8,1\n")
file:close()
local input = io.open("sales.csv", "r")
local rows = 0
local totalQty = 0
local totalRevenue = 0
local qtyBy = {}
local revenueBy = {}
for line in input:lines() do
local name, qty, price = line:match("^(%w+),(%d+),(%d+)$")
local q = tonumber(qty)
local r = q * tonumber(price)
rows = rows + 1
totalQty = totalQty + q
totalRevenue = totalRevenue + r
qtyBy[name] = (qtyBy[name] or 0) + q
revenueBy[name] = (revenueBy[name] or 0) + r
end
input:close()
print("Rows: " .. rows)
print("Total quantity: " .. totalQty)
print("Total revenue: " .. totalRevenue)
print("Average quantity: " .. totalQty / rows)
print("Average revenue: " .. totalRevenue / rows)
local products = {"apple", "banana", "cherry"}
for i = 1, #products do
local name = products[i]
print(name .. ": " .. qtyBy[name] .. ", " .. revenueBy[name])
endKey points
- Row: a row is one line of text holding fields separated by commas.
- Pattern: a pattern with captures splits one row into its separate fields.
- Parse: parsing turns the captured digit strings into numbers with tonumber.
- Aggregate: an aggregate combines many rows into totals, counts, and averages.
- Order: a fixed list of product names sets the report order, so the report never depends on table iteration order.
In this example, the tool first writes four sales rows to a file, then reads the file back line by line. Each line is split by a pattern, its numbers are turned into numbers with tonumber, and the totals and per-product sums grow row by row. The report prints the overall totals and averages, then one line per product in a fixed product order.
Note
the tool writes the file it reads, so it never depends on a file from outside the program.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Which pattern splits the row "apple,10,2" into its name and its two numbers?