How do you guys Test for NIL values when dealing with a lot of nested arrays?

Posted by Mendaloth on Wed 23 Nov 2011 07:27 PM — 5 posts, 23,314 views.

#0
I am wondering what is the best way or how do you guys normally deal with testing to see if an array is nil.

Take my current example. I have an array with a bunch subarrays:
filter_settings['clan_settings'][clan_name]['selected'] = "Y"
filter_settings['clan_settings'][clan_name]['player_clan'] = "Y"

There are 3 or 4 different functions that either change these values or display them for the user so there is no guarantee that it is not NIL.

I know anytime I access or change an array I can test if it is nil, and then create the array if it does not already exist. I don't like that approach in the above case because I end up having to do a lot of ifs. Even if I assume the clan_settings array already exists within the filter_settings array, I still have to check if filter_settings['clan_settings'][clan_name] is nil, and then if either filter_settings['clan_settings'][clan_name]['selected']/['player_clan'] are nil.

Doing this 3 or 4 times gets messy.

I came up with the idea of just testing to see if the filter_settings['clan_settings'][clan_name] is nil, and if it is nil then filling the array with default values for every clan OnPluginInstall. This works for now, but if I later decide I want to add a third value like filter_settings['clan_settings'][clan_name]['never_select'] then my plugin will have to back in and go through every clan name and make sure there is a default value for the never_select. If I end up adding a few different values after I first published the plugin then it ends up looking like a mess.

Am I missing a third option, or an easier way of doing this?
Amended on Wed 23 Nov 2011 07:40 PM by Mendaloth
Australia Forum Administrator #1
In your case you really only have one variable, clan_name. That is, I assume you start off with:

filter_settings.clan_settings
being a table that exists.


After that you only really need one line


filter_settings.clan_settings [clan_name] = filter_settings.clan_settings [clan_name] or {}


That guarantees that clan's table exists. Now you can just do:


filter_settings.clan_settings.[clan_name].player_clan = "Y"


Where, of course, player_clan being nil means the same as no. In which case you are better off using booleans:


filter_settings.clan_settings.[clan_name].player_clan = true


Then you can say:


if filter_settings.clan_settings.[clan_name].player_clan then
  --  player has a clan
end -- if


... because the absence of the variable (ie. nil) is the same as false.
Amended on Wed 23 Nov 2011 08:47 PM by Nick Gammon
Australia Forum Administrator #2
Or you can simplify things for yourself by making a metatable.




-- set up tables:

filter_settings = {}
filter_settings.clan_settings = {}

mt = { __index = 
     function (tbl, key) 
       tbl [key] = {}    -- add sub table
       return tbl [key]  -- return as missing value
     end }

setmetatable (filter_settings.clan_settings, mt)


What this does is (assuming you have the tables filter_settings and clan_settings set up initially), detects if you attempt to access a non-existent clan, and add that clan to the table on-the-fly.

Now you can just access the new clan:



clan_name = "foo"

filter_settings.clan_settings [clan_name].selected = true

require "tprint"
tprint (filter_settings)


Output:


"clan_settings":
  "foo":
    "selected"=true



Amended on Wed 23 Nov 2011 08:51 PM by Nick Gammon
USA #3
You could also try using a function to go down a chain of tables, inserting new ones if necessary.

function table_set_chain(tbl, k, v, next, ...)
  if type(tbl) ~= "table" then
    error("`" .. tostring(tbl) .. "` is not a table")
  end
  
  if next then
    local child = tbl[k]
    if not child then
      child = {}
      tbl[k] = child
    end
    return table_set_chain(child, v, next, ...)
  else
    tbl[k] = v
    return true
  end
end


Usage: table_set_chain(filter_settings, 'clan_settings', clan_name, 'selected', "Y")

Yay tail recursion.
Amended on Wed 23 Nov 2011 09:02 PM by Twisol
#4
Nick Gammon said:


Then you can say:


if filter_settings.clan_settings.[clan_name].player_clan then
  --  player has a clan
end -- if


... because the absence of the variable (ie. nil) is the same as false.



Thanks that little trick was exactly what I was missing! Works great now!