Added rowTable option to ftcsv.parse()

rowTable makes ftcsv.parse() store rows in an existing table, which
allows merging of multiple CSV files.
This commit is contained in:
Chris Smith 2017-11-02 13:52:08 +00:00
parent 4a2c22fb0a
commit e5b52d0eb1
3 changed files with 42 additions and 8 deletions

View File

@ -88,6 +88,19 @@ ftcsv.parse("apple,banana,carrot", ",", {loadFromString=true, headers=false})
In the above example, the first field becomes 'a', the second field becomes 'b' and so on.
- `rowTable`
If you want to merge multiple CSV files, you can set `rowTable` to be an existing table to which ftcsv will append new rows.
Note: headers are not compared between multiple datasets. If there is a mismatch between headers of different CSV files, then rows from each CSV file will have missing fields.
```lua
local options = {loadFromString=true, rename={["a"] = "d", ["b"] = "e", ["c"] = "f"}}
local actual = ftcsv.parse("a,b,c\r\napple,banana,carrot", ",", options)
options.rowTable = actual
ftcsv.parse("a,b,c\r\ndamson,elderberry,fig", ",", options)
```
For all tested examples, take a look in /spec/feature_spec.lua

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(outResults, inputString, inputLength, delimiter, i, headerField, fieldsToKeep)
-- keep track of my chars!
local currentChar, nextChar = sbyte(inputString, i), nil
@ -141,7 +141,6 @@ local function parseString(inputString, inputLength, delimiter, i, headerField,
local delimiterByte = sbyte(delimiter)
local assignValue
local outResults
-- the headers haven't been set yet.
-- aka this is the first run!
if headerField == nil then
@ -153,8 +152,9 @@ local function parseString(inputString, inputLength, delimiter, i, headerField,
end
else
-- print("this is for magic")
outResults = {}
outResults[1] = {}
outResults = outResults or {}
lineNum = #outResults + 1
outResults[lineNum] = {}
assignValue = function()
if not pcall(function()
outResults[lineNum][headerField[fieldNum]] = field
@ -300,6 +300,7 @@ function ftcsv.parse(inputFile, delimiter, options)
local fieldsToKeep = nil
local loadFromString = false
local headerFunc
local rowTable = {}
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 +331,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.rowTable ~= nil then
assert(type(options.rowTable) == "table", "ftcsv only takes in a table for the optional parameter 'rowTable'. You passed in '" .. tostring(options.rowTable) .. "' of type '" .. type(options.rowTable) .. "'.")
rowTable = options.rowTable
end
end
-- handle input via string or file!
@ -347,7 +352,7 @@ function ftcsv.parse(inputFile, delimiter, options)
end
-- parse through the headers!
local headerField, i = parseString(inputString, inputLength, delimiter, 1)
local headerField, i = parseString(nil, inputString, inputLength, delimiter, 1)
i = i + 1 -- start at the next char
-- make sure a header isn't empty
@ -389,8 +394,8 @@ function ftcsv.parse(inputFile, delimiter, options)
end
end
local output = parseString(inputString, inputLength, delimiter, i, headerField, fieldsToKeep)
return output
rowTable = parseString(rowTable, inputString, inputLength, delimiter, i, headerField, fieldsToKeep)
return rowTable
end
-- a function that delimits " to "", used by the writer

View File

@ -21,6 +21,22 @@ describe("csv features", function()
assert.are.same(expected, actual)
end)
it("should handle merging multiple datasets", function()
local expected = {}
expected[1] = {}
expected[1].a = "apple"
expected[1].b = "banana"
expected[1].c = "carrot"
expected[2] = {}
expected[2].b = "banana"
expected[2].c = "carrot"
expected[2].d = "damson"
expected[2].e = "elderberry"
local actual = ftcsv.parse("a,b,c\napple,banana,carrot", ",", {loadFromString=true})
ftcsv.parse("b,c,d,e\nbanana,carrot,damson,elderberry", ",", {loadFromString=true, rowTable=actual})
assert.are.same(expected, actual)
end)
it("should handle renaming a field", function()
local expected = {}
expected[1] = {}