Save/Load a Table

I was wondering if anyone knew a way to save and load tables? Since they are not supported by saveLocalData, etc.

Yep, you need to flatten (“serialise”) them into a string, then you can save them into local data.

I do it in this game tutorial to create an undo function (see “multiple undo” heading)

http://coolcodea.wordpress.com/2013/03/31/19-lessons-from-a-simple-board-game-part-2/

I do it the lazy way and use json but thats overkill. Check out ignatz’s tutorial on debugging there is a dump class that i bet could be setup to decode a string then write an encoder.

I literally just finished the code for this in my game and was thinking about posting it. I’ll post it tomorrow.

@Zoyt, awesome

@JakAttak - All right, Here we go:
Here is my function I altered from the Programming in Lua book for converting a table to an executable string:

function tToS (name, value, saved)
    local function basicSerialize (o)
        if type(o) == "number" then
            return tostring(o)
        else -- assume it is a string
            return string.format("%q", o)
        end
    end
    saved = saved or {}
    local returnStr = name.." = " 
    if type(value) == "number" or type(value) == "string" then
        returnStr = returnStr..basicSerialize(value).."\
"
    elseif type(value) == "table" then
        if saved[value] then
            returnStr = returnStr..saved[value].."\
"
        else
            saved[value] = name
            returnStr = returnStr.."{}\
"
            for k,v in pairs(value) do 
                local fieldname = string.format("%s[%s]", name, basicSerialize(k))
                returnStr = returnStr..tToS(fieldname, v, saved)
            end
        end
    else
        error("Cannot save a " .. type(value))
    end
    return returnStr
end

```

Then, to store the table, I say:  
table.insert(table,value)
saveProjectData("table",tToS("table",table))

```

Finally, to read and set the table, I say:  
table = readProjectData("table","table = {}")
assert(loadstring(table))()

```

Threre you have it. It uses the horrible loadString function, but it works well. I've looked into adding more data storage types like nils, vec2, touches, collisions, etc, but I haven't had the need for it yet. If anyone wants me to do that, just let me know.  
Thanks!  
P.S. @Ignatz - You migh suggest including this method somewhere in your tutorials. Lots of people use more than 2D tables. I use tables of all shapes and sizes.

@Zoyt thanks! This should be perfect for what I want

@JakAttak - No problem…

@Zoyt, having a little trouble getting it to work:

Here is the code I’m using to test it:


--# Main
-- String Table

-- Use this function to perform your initial setup
function setup()
jo = {{1,2,3},{2,3,5}}
saveProjectData("table",tToS("table",jo))
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)

    -- Do your drawing here
table = readProjectData("table")
assert(loadstring(table))()

if table == jo then
    print("It Worked")
end
end

function tToS(name, value, saved)
    local function basicSerialize (o)
        if type(o) == "number" then
            return tostring(o)
        else -- assume it is a string
            return string.format("%q", o)
        end
    end
    saved = saved or {}
    local returnStr = name.." = " 
    if type(value) == "number" or type(value) == "string" then
        returnStr = returnStr..basicSerialize(value).."\
"
    elseif type(value) == "table" then
        if saved[value] then
            returnStr = returnStr..saved[value].."\
"
        else
            saved[value] = name
            returnStr = returnStr.."{}\
"
            for k,v in pairs(value) do 
                local fieldname = string.format("%s[%s]", name, basicSerialize(k))
                returnStr = returnStr..tToS(fieldname, v, saved)
            end
        end
    else
        error("Cannot save a " .. type(value))
    end
    return returnStr
end

The first argument in tToS is the name of the table you’re putting in. So it should be “saveProjectData(“jo”,tToS(“jo”,jo))”. Hope that helps.

@Zoyt It still doesn’t work… Does it ever change the string back into a table?

Did you change the key to read the data to “Jo”? If so, can you post your code?

@Zoyt sure here it is:


--# Main
-- String Table

-- Use this function to perform your initial setup
function setup()
    jo = {{1,2,3},{2,3,5}}
    saveProjectData("jo",tToS("jo",jo))
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)

    -- Do your drawing here
table = readProjectData("jo", "jo = {}")
assert(loadstring(table))()

if table == jo then
    print("It Worked")
end
end

function tToS(name, value, saved)
    local function basicSerialize (o)
        if type(o) == "number" then
            return tostring(o)
        else -- assume it is a string
            return string.format("%q", o)
        end
    end
    saved = saved or {}
    local returnStr = name.." = " 
    if type(value) == "number" or type(value) == "string" then
        returnStr = returnStr..basicSerialize(value).."\
"
    elseif type(value) == "table" then
        if saved[value] then
            returnStr = returnStr..saved[value].."\
"
        else
            saved[value] = name
            returnStr = returnStr.."{}\
"
            for k,v in pairs(value) do 
                local fieldname = string.format("%s[%s]", name, basicSerialize(k))
                returnStr = returnStr..tToS(fieldname, v, saved)
            end
        end
    else
        error("Cannot save a " .. type(value))
    end
    return returnStr
end

@JakAttak - Right… My bad. I forgot that the tables have different identifiers. If you look at the individual values, you’ll see that they’re the same.
Hope that helps!

@Zoyt, actually I tried that

if table[1][1] == jo[1][1] 

But I get an error: attempting to index a nil value

I finally had the time to copy over your code. So sorry about that. The first field you pass into tTooS should be “table”, not “jo”. Also, it’s not a good technique to override Lua libraries, like the table one. Hope that works.

@Zoyt, so saveProjectData(“jo”, tToS(“table”, “jo”)?

function setup()
    jo = {{1,2,3},{2,3,5}}
    saveProjectData("jo",tToS("t",jo))
end

function draw()
    background(40, 40, 50)
    
    t = readProjectData("jo", "t = {}")
    assert(loadstring(t))()

    if t[1][1] == jo[1][1] then
        print("It Worked")
    end
end

function tToS(name, value, saved)
    local function basicSerialize (o)
        if type(o) == "number" then
            return tostring(o)
        else -- assume it is a string
            return string.format("%q", o)
        end
    end
    saved = saved or {}
    local returnStr = name.." = " 
    if type(value) == "number" or type(value) == "string" then
        returnStr = returnStr..basicSerialize(value).."\
"
    elseif type(value) == "table" then
        if saved[value] then
            returnStr = returnStr..saved[value].."\
"
        else
            saved[value] = name
            returnStr = returnStr.."{}\
"
            for k,v in pairs(value) do 
                local fieldname = string.format("%s[%s]", name, basicSerialize(k))
                returnStr = returnStr..tToS(fieldname, v, saved)
            end
        end
    else
        error("Cannot save a " .. type(value))
    end
    return returnStr
end

```

I got it working. The “t = {}” doesn’t appear to be necessary

@JakAttak - That is correct, but when I store project data, I read it before I write anything to it.