Length of a set

Hi all,

I am relatively new to lua and Corona SDK. I have a table that contains tuples of x,y and create two sets that contain x and y values separately. Interestingly length of  set x is correct and length of set y is always 0. I amsure this is not a bug and I am doing something stupid. Thanks in advance.

local lettersPlayed = { {1,5}, {2,5} } local setX = {} local setY = {} for i,v in ipairs(lettersPlayed) do setX[v[1]] = true setY[v[2]] = true end print("SetX size: " .. #setX) print("SetY size: " .. #setY)

Note: The below is in part inferred from experimentation

Consider this:

local myTable = {} myTable[1] = "hello" myTable[2] = "there" myTable[5] = "hurrah" print(#myTable) --for i, j in ipairs(myTable) do --    print(i, j) --end for k, v in pairs(myTable) do     print(k, v) end

When you use pairs, it prints out every key and every value in the table as expected. But when you use ipairs, it only prints out the first two.

Notice too that the two that ipairs prints out are keys 1 and 2. When you extend a table as above with brackets [] and a number, it seems one of two things can happen:

  •  The index already exists, and you overwrite the value, or the index is one more than the current lenght of the table and the table is added  to accordingly
  • The key is a number greater  than the length of the table + 1, it makes no sense to add it to the list part of the table, so it gets treated as a key for indexing the dictionary part of the table.

What I said is probably a bit confusing and it comes down to what I mean by “list part” and “dictionary part”. A lua table is a bit of a chimera. It’s in one part a simple list. If you want to make a list, you just list things inside curly braces: { “a”, “b”, “c”, true, 100}.

If you want to get the fifth value in the list, you do it like: myTable[5].

On the other hand, lua tables are like dictionaries. You can make a dictionary with curly braces containg key, value pairs:

{a = 1, b = 2}.

You can access this similarly: myTable[“a”] or myTable.a --> either gives you 1

Using a table as a dictionary, you give a key word( which can be a string, number, table, and probably other types), and you get its value, or “definition” if using the dictionary analogy. The phenomenon you’ve encountered is a consequence of tables being both lists and dictionaries. If you index into a table to add something, and the index is between 1 and the lenght of the “list part” of the table + 1, then it makes sense that you are either changing what exists at a given position in the list part, or extending it( if the index is table length + 1). Otherwise it doesn’t make sense that you would be altering the list part, so it’s treated as an action on the dictionary part of the table.

This is the difference between pairs() and ipairs(). ipairs exclusively accesses the list part of the table, whereas pairs access both parts.

So, in your code, the first modification you make to a table is:

setX[1] = true

Since 1 is “between 1 and the lenght of table + 1”, it makes sense that you are putting something in the first position of the list and this is what happens.

Next, however you end up with:

setY[5] = true

Since 5 is not “between 1 and the lenght of table + 1”, it does not makes sense that you are adding to the “list part” of the table, so it gets treated as a dictionary entry.

Frankly, I don’t know why they don’t just have this throw an error, as I don’t see the utility of it, especially when it will disappear if the list expands to 5 or more items in length. Hopefully, someone else might be able to enlighten us.

Edit: It’s worth pointing out as well that when you get the length of a table with #, it gives you the lenght of the “list part” only.

If anyone needs a shorter answer for why the length of SetY is 0: 

The # operator stops counting when it hits a nil value.

local myTable = {} myTable[1] = "hello" myTable[2] = "world" myTable[3] = "foo" myTable[9999] = "bar" print(#myTable) --output will be 3

In the example above, the #operator will count up from 0 until it hits nil which will be after index 3. It doesn’t matter that there is an index 9999 because index 4 was nil.

In your code, SetY[1] is nil so it returns the length as 0.

@hasty - you asked why this is allowed. Sometimes it can be useful to have nil entries in a table, in order to verify whether something exists or not. This can make it quick to check for a table entry rather than looping through an entire table to check if an id exists:

Compare this:

local numberToFind = 23 if myTable[numberToFind] then doSomething() end

to this:

local numberToFind = 23 for i = 1, #myTable do if myTable[i].id == numberToFind then doSomething() end end

Note: The below is in part inferred from experimentation

Consider this:

local myTable = {} myTable[1] = "hello" myTable[2] = "there" myTable[5] = "hurrah" print(#myTable) --for i, j in ipairs(myTable) do --    print(i, j) --end for k, v in pairs(myTable) do     print(k, v) end

When you use pairs, it prints out every key and every value in the table as expected. But when you use ipairs, it only prints out the first two.

Notice too that the two that ipairs prints out are keys 1 and 2. When you extend a table as above with brackets [] and a number, it seems one of two things can happen:

  •  The index already exists, and you overwrite the value, or the index is one more than the current lenght of the table and the table is added  to accordingly
  • The key is a number greater  than the length of the table + 1, it makes no sense to add it to the list part of the table, so it gets treated as a key for indexing the dictionary part of the table.

What I said is probably a bit confusing and it comes down to what I mean by “list part” and “dictionary part”. A lua table is a bit of a chimera. It’s in one part a simple list. If you want to make a list, you just list things inside curly braces: { “a”, “b”, “c”, true, 100}.

If you want to get the fifth value in the list, you do it like: myTable[5].

On the other hand, lua tables are like dictionaries. You can make a dictionary with curly braces containg key, value pairs:

{a = 1, b = 2}.

You can access this similarly: myTable[“a”] or myTable.a --> either gives you 1

Using a table as a dictionary, you give a key word( which can be a string, number, table, and probably other types), and you get its value, or “definition” if using the dictionary analogy. The phenomenon you’ve encountered is a consequence of tables being both lists and dictionaries. If you index into a table to add something, and the index is between 1 and the lenght of the “list part” of the table + 1, then it makes sense that you are either changing what exists at a given position in the list part, or extending it( if the index is table length + 1). Otherwise it doesn’t make sense that you would be altering the list part, so it’s treated as an action on the dictionary part of the table.

This is the difference between pairs() and ipairs(). ipairs exclusively accesses the list part of the table, whereas pairs access both parts.

So, in your code, the first modification you make to a table is:

setX[1] = true

Since 1 is “between 1 and the lenght of table + 1”, it makes sense that you are putting something in the first position of the list and this is what happens.

Next, however you end up with:

setY[5] = true

Since 5 is not “between 1 and the lenght of table + 1”, it does not makes sense that you are adding to the “list part” of the table, so it gets treated as a dictionary entry.

Frankly, I don’t know why they don’t just have this throw an error, as I don’t see the utility of it, especially when it will disappear if the list expands to 5 or more items in length. Hopefully, someone else might be able to enlighten us.

Edit: It’s worth pointing out as well that when you get the length of a table with #, it gives you the lenght of the “list part” only.

If anyone needs a shorter answer for why the length of SetY is 0: 

The # operator stops counting when it hits a nil value.

local myTable = {} myTable[1] = "hello" myTable[2] = "world" myTable[3] = "foo" myTable[9999] = "bar" print(#myTable) --output will be 3

In the example above, the #operator will count up from 0 until it hits nil which will be after index 3. It doesn’t matter that there is an index 9999 because index 4 was nil.

In your code, SetY[1] is nil so it returns the length as 0.

@hasty - you asked why this is allowed. Sometimes it can be useful to have nil entries in a table, in order to verify whether something exists or not. This can make it quick to check for a table entry rather than looping through an entire table to check if an id exists:

Compare this:

local numberToFind = 23 if myTable[numberToFind] then doSomething() end

to this:

local numberToFind = 23 for i = 1, #myTable do if myTable[i].id == numberToFind then doSomething() end end