Persistant storage: single long string or key-value?

I’d like to save user-created objects that each have a handful of properties. There are two ways I can think of to do this. One is to give each object an id, and then store each property as a key-value using something like objectID_propertyName, property. The other is to make a long string similar to how CargoBot handles level solutions. Objects each have their own line and properties are enclosed in ().

Which solution makes more sense in terms of memory and speed? I like the first solution because accessing the property is more intuitive and assuming the underlying storage is a hash table, it should be faster than string manipulations, right? But I am not thrilled about assigning each object an ID. It’s not hard to generate, but doesn’t have much to do with the object except for data storage. And I still have to scrape the id to get the property name, which might take as long as processing one long string.

Which approach is better? Is there a third, more sensible way to handle it?

And, while I’m worrying about memory usage, is there a tool to track memory used by my app?

Thanks!

So, thinking about this some more, I’m now leaning toward using an object id for the key and a single string for the properties: objectID, “property1, property2, property3”. I’ll still have to process the string to get individual properties, but this likely would only happen at the beginning of the app. This way, I can save object data when the user creates the object. If I have one long, multiple line string with properties for all the objects, I’d either have to keep rewriting the whole thing; or wait until the game exits to save it. I’ll still have to give each object having an id though.

This tells you memory usage:

function showMem()   
    local kb = gcinfo()
    print(tostring(kb).." kb")
end

I think i would save an executable string:


-- test loadstring

-- Use this function to perform your initial setup
function setup()
    -- object to load
    obj = {}
    -- string saving obj properties
    str = 
[[
    obj.color = color(255, 0, 0, 255)
    obj.size = 100
]]

    parameter.action("show properties",
        function()
            print("properties:")
            for i,v in pairs(obj) do 
            print(tostring(i).." : "..tostring(v))
            end
            print("end")
        end
    )
    parameter.action("load properties",
        function()
            loadstring(str)()
        end
    )
end

This programs shows the properties are undefined until you load them. Then they are there. Check the [[ syntax for multiline strings. Also check the special syntaxt for loadstring! You can save the string either in one local variable, or in a tab (better for exchanging your program with others).

I don’t tend to save executable code, but save properties to delineated strings. Often I find the easiest thing to do when reading them in is populate a table of string segments using something like this for semicolon delimited values

i = 0
for t in string.gmatch(s,"([^;]+)") do
    i = i + 1
    bits[i] = t
end

and so on…

Then come back to that table and figure out what goes where based on some index values.

Thanks for the suggestions. Very helpful!

So it sounds like you would both recommend saving one key-value per object, but Jvm would go with an executable string and Mark would use a regular string and then parse it out. I hadn’t considered using an executable - kind of new to Lua and wasn’t even clear on whether Codea used a subset of Lua or the whole thing. It does nicely avoid having to assign an order to the properties. Either way, I would still need to use some sort of object ID to use as the key, right?

Jvm, when you say ‘tabs’, are you referring to the tabs in the coding environment? The documentation for readProjectData mentions sharing projects. I had assumed that meant between developers, like for code sharing. Someone who downloads the app wouldn’t have access to the tabs, would they? Also, can I programmatically write to a tab?

I thought you were knew to lua, this is why i showed an example with some aspects it took me some time to learn. You can save into a tab with saveProjectTab, see the doc of codea for details. ‘yes’ to your other questions.

I found this online and it has worked rather well. It will save the table to a string then I just loadstring it into a table,

function table.val_to_str ( v )
  if "string" == type( v ) then
    v = string.gsub( v, "\
", "\\\
" )
    if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
      return "'" .. v .. "'"
    end
    return '"' .. string.gsub(v,'"', '\\\"' ) .. '"'
  else
    return "table" == type( v ) and table.tostring( v ) or
      tostring( v )
  end
end

function table.key_to_str ( k )
  if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
    return k
  else
    return "[" .. table.val_to_str( k ) .. "]"
  end
end

function table.tostring( tbl )
  local result, done = {}, {}
  for k, v in ipairs( tbl ) do
    table.insert( result, table.val_to_str( v ) )
    done[ k ] = true
  end
  for k, v in pairs( tbl ) do
    if not done[ k ] then
      table.insert( result,
        table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
    end
  end
  return "{" .. table.concat( result, "," ) .. "}"
end