Array: Difference between # and maxn

Hello, I noticed some strange differences :

Here # and maxn give different values: # stops to the 1st nil and maxn gives the number of elements until it reachs the last non nil value. It seems logical…

t={}
t[1]=nil
t[2]=nil
t[3]=1
t[4]=nil
print(#t, table.maxn(t)) --=> 0,3

Here both have the same behaviour than previously but where’s the logic about # in the 2nd example, it should be 0 instead of 2:

t={nil,nil,1,nil}
print(#t, table.maxn(t)) --=> 0,3

t={nil,1,nil,nil}
print(#t, table.maxn(t)) --=> 2 ,2

What do you think about all this?

very interesting indeed !

the following inconsistency is reproducible with 1 in any position. just add more nils.

t={nil,1} print(#t, table.maxn(t)) --=\> 2,2 t={nil,1,nil} print(#t, table.maxn(t)) --=\> 0,2 t={nil,1,nil,nil} print(#t, table.maxn(t)) --=\> 2,2 t={nil,1,nil,nil,nil} print(#t, table.maxn(t)) --=\> 2,2 t={nil,1,nil,nil,nil,nil} print(#t, table.maxn(t)) --=\> 0,2 t={nil,1,nil,nil,nil,nil,nil} print(#t, table.maxn(t)) --=\> 0,2 t={nil,1,nil,nil,nil,nil,nil,nil} print(#t, table.maxn(t)) --=\> 2,2

here’s what i can surmise from some research:

tables in Lua, at their heart, are really associative arrays, an object (class). indicies for stored values can be numbers, strings, etc, but NOT ‘nil’.
tables used as arrays are not “pure” arrays as we think of in languages like C. they are a construct made from an associative array, thus it’s possible to run into issues when wanting to store a ‘nil’ as a valid value.

http://www.lua.org/pil/2.5.html

however, from some strict definitions of the ‘#’ operator, it seems like it should still work, or at least be consistent based on our small examples above:

“The length operator # only applies from 1 to the last non-nil element.”
http://www.luafaq.org/gotchas.html#T6.1

but from official docs, it seems like that behavior isn’t guaranteed:

“the length of a table t **is only defined** if the table is a sequence, … Note that a table like
{10, 20, nil, 40}
is not a sequence, because it has the key 4 but does not have the key 3.”
http://www.lua.org/manual/5.2/manual.html#3.4.6

many pages suggest avoid using these “sparse arrays” to bypass the issue:

“Try not to put nil in arrays. It will work if you know what you’re doing, but the length operator # will be confused and standard table functions like table.sort() will complain bitterly. Sparse arrays have their uses, if you remember not to depend on #”
http://www.luafaq.org/gotchas.html#T6.4

“If you have a table like {‘a’, ‘b’, nil, ‘d’, ‘e’} then that is *not* considered to be treating a Lua table as an array, and therefore, anything you try to do on that table as if it *was an array (for example: getting its length with #, … are not guaranteed to work”
http://lua-for-ags.wikispaces.com/IntroducingLuaDataDefinition

some better news is that there was a long discussion back in 2007 which addressed this issue: language inconsistencies, confusion for newbies, and possible short- and long-term solutions for Lua. one short-term solution is to use another value to represent ‘nil’ in your code – e.g., ‘false’ would work, as would something like this:

NIL = {} -- we create our own 'NIL' t={NIL,1,NIL} print(#t, table.maxn(t)) --=\> 3,3

http://lua-users.org/lists/lua-l/2007-07/msg00557.html

so that’s what i’ve discovered. take what you wish and choose your own path. :slight_smile:

cheers,
dmc

ps, thanks @waltsir for the topic. :slight_smile:

Thank you very much for all the details! It’s strange that in my app I already used {} instead of nil without knowing it could be a work around but the most important thing is like you said, using nil is ok only if we know exactly what we do:)

very interesting indeed !

the following inconsistency is reproducible with 1 in any position. just add more nils.

t={nil,1} print(#t, table.maxn(t)) --=\> 2,2 t={nil,1,nil} print(#t, table.maxn(t)) --=\> 0,2 t={nil,1,nil,nil} print(#t, table.maxn(t)) --=\> 2,2 t={nil,1,nil,nil,nil} print(#t, table.maxn(t)) --=\> 2,2 t={nil,1,nil,nil,nil,nil} print(#t, table.maxn(t)) --=\> 0,2 t={nil,1,nil,nil,nil,nil,nil} print(#t, table.maxn(t)) --=\> 0,2 t={nil,1,nil,nil,nil,nil,nil,nil} print(#t, table.maxn(t)) --=\> 2,2

here’s what i can surmise from some research:

tables in Lua, at their heart, are really associative arrays, an object (class). indicies for stored values can be numbers, strings, etc, but NOT ‘nil’.
tables used as arrays are not “pure” arrays as we think of in languages like C. they are a construct made from an associative array, thus it’s possible to run into issues when wanting to store a ‘nil’ as a valid value.

http://www.lua.org/pil/2.5.html

however, from some strict definitions of the ‘#’ operator, it seems like it should still work, or at least be consistent based on our small examples above:

“The length operator # only applies from 1 to the last non-nil element.”
http://www.luafaq.org/gotchas.html#T6.1

but from official docs, it seems like that behavior isn’t guaranteed:

“the length of a table t **is only defined** if the table is a sequence, … Note that a table like
{10, 20, nil, 40}
is not a sequence, because it has the key 4 but does not have the key 3.”
http://www.lua.org/manual/5.2/manual.html#3.4.6

many pages suggest avoid using these “sparse arrays” to bypass the issue:

“Try not to put nil in arrays. It will work if you know what you’re doing, but the length operator # will be confused and standard table functions like table.sort() will complain bitterly. Sparse arrays have their uses, if you remember not to depend on #”
http://www.luafaq.org/gotchas.html#T6.4

“If you have a table like {‘a’, ‘b’, nil, ‘d’, ‘e’} then that is *not* considered to be treating a Lua table as an array, and therefore, anything you try to do on that table as if it *was an array (for example: getting its length with #, … are not guaranteed to work”
http://lua-for-ags.wikispaces.com/IntroducingLuaDataDefinition

some better news is that there was a long discussion back in 2007 which addressed this issue: language inconsistencies, confusion for newbies, and possible short- and long-term solutions for Lua. one short-term solution is to use another value to represent ‘nil’ in your code – e.g., ‘false’ would work, as would something like this:

NIL = {} -- we create our own 'NIL' t={NIL,1,NIL} print(#t, table.maxn(t)) --=\> 3,3

http://lua-users.org/lists/lua-l/2007-07/msg00557.html

so that’s what i’ve discovered. take what you wish and choose your own path. :slight_smile:

cheers,
dmc

ps, thanks @waltsir for the topic. :slight_smile:

Thank you very much for all the details! It’s strange that in my app I already used {} instead of nil without knowing it could be a work around but the most important thing is like you said, using nil is ok only if we know exactly what we do:)