Improve the csv parser by reading header and assigning values to their header

So we have things like row[1] AND ALSO row["foobar"]
And also row.foobar of course
This commit is contained in:
2025-11-15 15:39:49 +01:00
parent 50455c491d
commit 3e552428a5

View File

@@ -223,12 +223,16 @@ end
--- ---
--- @param csv string The CSV text to parse. --- @param csv string The CSV text to parse.
--- @param delimiter string? The field delimiter (default: ","). --- @param delimiter string? The field delimiter (default: ",").
--- @return table A table (array) of rows; each row is a table (array) of string fields. --- @param hasHeaders boolean? If true, first row is treated as headers and rows can be accessed by header name (default: false).
function fromCSV(csv, delimiter) --- @return table A table (array) of rows; each row is a table with numeric indices and optionally header-named keys.
function fromCSV(csv, delimiter, hasHeaders)
if delimiter == nil then if delimiter == nil then
delimiter = "," delimiter = ","
end end
local rows = {} if hasHeaders == nil then
hasHeaders = false
end
local allRows = {}
local fields = {} local fields = {}
local field = {} local field = {}
@@ -254,7 +258,7 @@ function fromCSV(csv, delimiter)
elseif c == '\r' or c == '\n' then elseif c == '\r' or c == '\n' then
table.insert(fields, table.concat(field)) table.insert(fields, table.concat(field))
field = {} field = {}
table.insert(rows, fields) table.insert(allRows, fields)
fields = {} fields = {}
if c == '\r' and i < len and csv:sub(i + 1, i + 1) == '\n' then if c == '\r' and i < len and csv:sub(i + 1, i + 1) == '\n' then
i = i + 2 i = i + 2
@@ -286,7 +290,7 @@ function fromCSV(csv, delimiter)
elseif c == '\r' or c == '\n' then elseif c == '\r' or c == '\n' then
table.insert(fields, table.concat(field)) table.insert(fields, table.concat(field))
field = {} field = {}
table.insert(rows, fields) table.insert(allRows, fields)
fields = {} fields = {}
state = STATE_DEFAULT state = STATE_DEFAULT
if c == '\r' and i < len and csv:sub(i + 1, i + 1) == '\n' then if c == '\r' and i < len and csv:sub(i + 1, i + 1) == '\n' then
@@ -303,10 +307,27 @@ function fromCSV(csv, delimiter)
if #field > 0 or #fields > 0 then if #field > 0 or #fields > 0 then
table.insert(fields, table.concat(field)) table.insert(fields, table.concat(field))
table.insert(rows, fields) table.insert(allRows, fields)
end end
return rows if hasHeaders and #allRows > 0 then
local headers = allRows[1]
local rows = {}
for i = 2, #allRows do
local row = {}
local dataRow = allRows[i]
for j = 1, #dataRow do
row[j] = dataRow[j]
if headers[j] ~= nil and headers[j] ~= "" then
row[headers[j]] = dataRow[j]
end
end
table.insert(rows, row)
end
return rows
end
return allRows
end end
--- Converts a table of rows back to CSV text format (RFC 4180 compliant). --- Converts a table of rows back to CSV text format (RFC 4180 compliant).
@@ -676,7 +697,7 @@ STRING FUNCTIONS:
format(s, ...) - Formats string using Lua string.format format(s, ...) - Formats string using Lua string.format
trim(s) - Removes leading/trailing whitespace trim(s) - Removes leading/trailing whitespace
strsplit(inputstr, sep) - Splits string by separator (default: whitespace) strsplit(inputstr, sep) - Splits string by separator (default: whitespace)
fromCSV(csv, delimiter) - Parses CSV text into rows of fields (delimiter defaults to ",") fromCSV(csv, delimiter, hasHeaders) - Parses CSV text into rows of fields (delimiter defaults to ",", hasHeaders defaults to false)
toCSV(rows, delimiter) - Converts table of rows to CSV text format (delimiter defaults to ",") toCSV(rows, delimiter) - Converts table of rows to CSV text format (delimiter defaults to ",")
num(str) - Converts string to number (returns 0 if invalid) num(str) - Converts string to number (returns 0 if invalid)
str(num) - Converts number to string str(num) - Converts number to string