Added rowFunc as optional parameter to ftcsv.parse

rowFunc will be called after parsing each data row as follows:
  output[line] = rowFunc(output[line], line)

if output[line] is nil afterwards, line will not be incremented.
This commit is contained in:
Chris Smith 2017-11-01 17:15:49 +00:00
parent 4a2c22fb0a
commit 7bdd4aa5bd
3 changed files with 53 additions and 5 deletions

View File

@ -70,6 +70,22 @@ ftcsv.parse("apple,banana,carrot", ",", {loadFromString=true, headers=false})
local actual = ftcsv.parse("a,b,c\napple,banana,carrot", ",", options)
```
- `rowFunc`
Applies a function to every row in the dataset. `rowFunc` will be passed a table containing the data for the row, and the row number. It should return a table which will be used in place of the original data table, or `nil` to remove the row entirely.
Ex: making field `a` uppercase
```lua
local options = {
loadFromString=true,
rowFunc=function(row, num)
row.a = string.upper(row.a)
return row
end
}
local actual = ftcsv.parse("a,b,c\napple,banana,carrot", ",", options)
```
- `headers`
Set `headers` to `false` if the file you are reading doesn't have any headers. This will cause ftcsv to create indexed tables rather than a key-value tables for the output.

25
ftcsv.lua Normal file → Executable file
View File

@ -122,7 +122,7 @@ local function createField(inputString, quote, fieldStart, i, doubleQuoteEscape)
end
-- main function used to parse
local function parseString(inputString, inputLength, delimiter, i, headerField, fieldsToKeep)
local function parseString(inputString, inputLength, delimiter, i, headerField, fieldsToKeep, rowFunc)
-- keep track of my chars!
local currentChar, nextChar = sbyte(inputString, i), nil
@ -240,11 +240,21 @@ local function parseString(inputString, inputLength, delimiter, i, headerField,
-- print("fs:", fieldStart)
end
-- incrememnt for new line
-- check the column count
if fieldNum < initialLineCount then
error('ftcsv: too few columns in row ' .. lineNum)
end
-- transform the row using the supplied function
if rowFunc then
outResults[lineNum] = rowFunc(outResults[lineNum], lineNum)
end
-- incrememnt for new line, if row transformation hasn't removed the current row.
if outResults[lineNum] then
lineNum = lineNum + 1
end
outResults[lineNum] = {}
fieldNum = 1
fieldStart = i + 1 + skipChar
@ -284,6 +294,10 @@ local function parseString(inputString, inputLength, delimiter, i, headerField,
-- otherwise there might not be enough line
elseif finalLineCount < initialLineCount then
error('ftcsv: too few columns in row ' .. lineNum)
-- else it is a fully-formed line, so do row transformation if required.
elseif rowFunc then
outResults[lineNum] = rowFunc(outResults[lineNum], lineNum)
end
return outResults
@ -300,6 +314,7 @@ function ftcsv.parse(inputFile, delimiter, options)
local fieldsToKeep = nil
local loadFromString = false
local headerFunc
local rowFunc
if options then
if options.headers ~= nil then
assert(type(options.headers) == "boolean", "ftcsv only takes the boolean 'true' or 'false' for the optional parameter 'headers' (default 'true'). You passed in '" .. tostring(options.headers) .. "' of type '" .. type(options.headers) .. "'.")
@ -330,6 +345,10 @@ function ftcsv.parse(inputFile, delimiter, options)
assert(type(options.headerFunc) == "function", "ftcsv only takes a function value for optional parameter 'headerFunc'. You passed in '" .. tostring(options.headerFunc) .. "' of type '" .. type(options.headerFunc) .. "'.")
headerFunc = options.headerFunc
end
if options.rowFunc ~= nil then
assert(type(options.rowFunc) == "function", "ftcsv only takes a function value for optional parameter 'rowFunc'. You passed in '" .. tostring(options.rowFunc) .. "' of type '" .. type(options.rowFunc) .. "'.")
rowFunc = options.rowFunc
end
end
-- handle input via string or file!
@ -389,7 +408,7 @@ function ftcsv.parse(inputFile, delimiter, options)
end
end
local output = parseString(inputString, inputLength, delimiter, i, headerField, fieldsToKeep)
local output = parseString(inputString, inputLength, delimiter, i, headerField, fieldsToKeep, rowFunc)
return output
end

View File

@ -158,6 +158,19 @@ describe("csv features", function()
assert.are.same(expected, actual)
end)
it("should make column A uppercase via rowFunc", function()
local expected = {}
expected[1] = {}
expected[1].a = "APPLE"
expected[1].b = "banana"
expected[1].c = "carrot"
local actual = ftcsv.parse("a,b,c\napple,banana,carrot", ",", {loadFromString=true, rowFunc=function(row)
row.a = string.upper(row.a)
return row
end})
assert.are.same(expected, actual)
end)
it("should handle encoding files", function()
local expected = {}
expected[1] = {}