Lua: implicit table creation with string keys - why the extra brackets? - lua

Say that you want to create a Lua table, and all its keys are valid lua identifiers. Then you can use the key=value syntax:
local niceTable = { I=1, like=1, this=1, syntax=1 }
If however your strings are not "identifiable", then you have to use the ['key']=value syntax:
local operators = { ['*']="Why", ['+']="the", ['/']="brackets", ['?']='?' }
I'm a bit baffled about this. What are those brackets doing there? What do they mean?

They identify the contained string as a key in the resulting table. The first form, you could consider as equal to
local niceTable = {}
niceTable.I = 1;
niceTable.like = 1;
The second form is equal to
local operators = {}
operators['*'] = "Why";
operators['+'] = "The";
The difference is purely syntactic sugar, except where the first one uses identifiers, so it has to follow the identifier rules, such as doesn't start with a number and interpret-time constant, and the second form uses any old string, so it can be determined at runtime, for example, and a string that's not a legal identifier. However, the result is fundamentally the same. The need for the brackets is easily explained.
local var = 5;
local table = {
var = 5;
};
-- table.var = 5;
Here, var is the identifier, not the variable.
local table = {
[var] = 5;
};
-- table[5] = 5;
Here, var is the variable, not the identifier.

The normal syntax for indexing a table is t[val]. For string keys only, Lua provides an alternate syntax, where t.foo is exactly equivalent to t["foo"]. This is purely a syntactical convenience, so-called 'syntax sugar'. It doesn't add functionality, it just gives you a less cluttered syntax for using strings as named fields.
There are a lot of strings keys this won't work for:
t["hello_world"] => t.hello_world -- works
t["hello world"] => t.hello world -- oops, space in the string
t["5 * 3"] => t.5 * 3 -- oops
t['[10]'] => t.[10] -- oops
Basically it only works if the string key would be a valid identifier.
Again, tables are indexed via [], and in most cases you need to use them:
t = {
-- [key] = value
[10] = "ten", -- number key, string value
["print function"] = print, -- string key, function value
["sub table"] = {}, -- string key, table value
[print] = 111, -- function key, number value
["foo"] = 123, -- string key, number value
}
Only if you're using a string key which would work as a valid identifier (no spaces, contains only word characters, numbers, or underlines, and doesn't begin with a number) can you use the shortcut syntax. For the table above, that would be only 'foo':
t = {
-- [key] = value
[10] = "ten", -- number key, string value
["print function"] = print, -- string key, function value
["sub table"] = {}, -- string key, table value
[print] = 111, -- function key, number value
foo = 123, -- string key, number value
}

Related

How do I get information from a table in lua?

I have some lua code like this:
_table = {
stuff = {
item1 = {Name="Stack",Rarity="Over"};
item2 = {Name="Flow",Rarity="Com"}
};
};
print("placeholder") -- example thing
_stuff = _table.stuff
for i = 1, #_stuff do
print(_stuff[i].Name)
end
The output is this:
placeholder
I've tried to look at stuff but I don't think it was related to my problem.
Lua tables conceptually are "maps": They map keys to values. Your table _table maps the key stuff to the {item1 = ..., item2 = ...} table. Your stuff table maps the string "item1" to the {Name="Stack",Rarity="Over"} table and the string "item2" to the {Name="Flow",Rarity="Com"} table.
The length operator # completely ignores string keys. It returns an integer i such that t[i] ~= nil and t[i+1] == nil. In particular, it returns 0 if there are no integer keys in the table. This is the case for your stuff table: You only have string keys. Thus your loop never runs, as the limit is 0 and i = 1 > 0 already is the case before the first loop iteration.
Put simply, you're trying to iterate over a "dictionary" (a table with string keys) as if it were a list. Lua tables have both a "list part" (integer keys from 1 to n, where usually n = #t) and a "hash part" (of all other keys). The "list part" is empty in your case, so there's nothing for you to iterate over.
To iterate over all key-value pairs of any table (list or dictionary) in an undefined order, use pairs (rather than ipairs or iterating over indices i, which only works for lists):
for itemname, item in pairs(_stuff) do
print(item.Name)
end
If you want a defined order of iteration, either build a second table of keys...
local order = {"item1", "item2"}
for _, key in ipairs(order) do
print(_stuff[key].Name)
end
... or use the integer keys ("list part") of the table:
_table = {
stuff = {
{key = "item1", Name="Stack", Rarity="Over"};
{key = "item2", Name="Flow", Rarity="Com"};
};
};
_stuff = _table.stuff
for _, key in ipairs(order) do
for i = 1, #_stuff do
print(_stuff[i].Name)
end
end
then works as expected.

Why isn't my LUA interpreter able to handle string key values?

When testing code with both a predefined script and the LUA runtime environment, LUA will not take any form of string key values. However, if a numerical value key is used LUA will work with it as intended. The exception to this rule when I am using Tshark with a LUA file to parse packet captures. This allows the string key value syntax to work normally. Is there something I may be performing wrong?
I have tried creating several .lua script files with different variations including:
testArray.NewItem = "value1"
testArray["NewItem"] = "value1"
NewItemValue = "NewItem"
testArray[NewItemValue] = "value1"
These all result in an nil value or an error due to trying to call a nil value depending on the return style used to check.
> tcpstream = {}
> stream1 = tostring(14356)
> tcpstream[stream1] = "nothing"
> print(#tcpstream)
0
> print(tcpstream[1])
nil
> tcpstream[1] = "nothing"
> print(#tcpstream)
1
> print(tcpstream[1])
nothing
the output of the print(#tcpstream) after the tcpstream[stream1] = "nothing" should show 1 not zero. The subsequent print(tcpstream[1]) should also show "nothing".
From http://lua-users.org/wiki/TablesTutorial
The # operator doesn't count all the items in the table (!). Instead it finds the last integer (non-fractional number) key. Because of how it's implemented its results are undefined if all the integer keys in the table aren't consecutive. Which is why it shouldn't be used for tables used as sparse arrays[2]).
The '#' is not a good(sometimes not correct) way to count the number of elements in Lua table.
As for
> stream1 = tostring(14356)
> tcpstream[stream1] = "nothing"
> print(#tcpstream)
0
> print(tcpstream[1])
nil
Lua uses key,value pairs, not explicitly index. If you do 'arr[1] = 22', it means the value for the key '1' is 22, not the value for the first element is 22.
The length operator(#) does not work as you believe, this is a common mistake for beginners in Lua.
The default behavior for #sometable is to return the number of consecutive key starting at the number 1(or after any nil value for 5.3). String keys are never evaluated with the default # operator for a table.
In 5.3 if your sequence contains multiple nil values the behavior of # is non-deterministic.
Lua 5.3 Reference Manual: 3.4.7 – The Length Operator
Lua 5.1 Reference Manual: 2.5.5 – The Length Operator
I will include the lines from 5.1 as i feel it covers the information regarding the operator and tables well. While note identical to how 5.3 work it maybe easier to understand why you see the behavior you do.
2.5.5 – The Length Operator
The length operator is denoted by the unary operator #. The length of a string is its number of bytes (that is, the usual meaning of string length when each character is one byte).
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil; moreover, if t1 is nil, n can be zero. For a regular array, with non-nil values from 1 to a given n, its length is exactly that n, the index of its last value. If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
Here are some examples of different table and their # results:
table1 = { --number keys in array
true,
true,
true,
}
table2 = { -- number keys in hash
[1] = true,
[2] = true,
[3] = true,
}
table3 = { -- only strings as key
['1'] = true,
['2'] = true,
['3'] = true,
}
table4 = { -- No key 2 defined
[1] = true,
-- [2] = true,
[3] = true,
}
table5 = { -- table with both string and number keys
[1] = true,
['2'] = true,
}
print(#table1) -- 3
print(#table2) -- 3
print(#table3) -- 0
print(#table4) -- v5.3(1 or 3) v5.1(1)
print(#table5) -- 1

Using strings containing integers as table keys

I realize this is usually not a great practice, but how would I use a string containing an integer (e.g. "7") as a table key? For example:
local myTable = {
"1" = "Foo",
"2" = "Bar"
}
If memory serves from reading the Lua manual back in the day, that should be possible with some special syntax, but what I've written above is a syntax error.
Like this:
local myTable = {
["1"] = "Foo",
["2"] = "Bar"
}
Because the keys are not valid identifiers, you can't use the syntax sugar form.

Lua Changing Table Keys

Anyone tell me why this doesn't work?
GET_TABLE {1=ID}
key = string.format("%q", GET_TABLE[1])
RETURN_TABLE[key] = "ss"
print(RETURN_TABLE[ID])
print(GET_TABLE[1])
First print result: nil. Second print result: ID
I want the first print result to be: ss
GET_TABLE {1=ID}
key = "ID"
RETURN_TABLE[key] = "ss"
print(RETURN_TABLE[ID])
print(GET_TABLE[1])
The above works fine so I assume its due to the string.format not working right?
The %q format token returns the input as an escaped and quoted Lua string. This means that given the input ID it will return "ID" (the double quotes being part of the string!) which is a different string. (Or, represented as Lua strings, the input is 'ID' and the return value is '"ID"'.)
You have therefore set the ID key while trying to retrieve the "ID" key (which presumably does not exist).
> x = 'ID'
> =x
ID
> =string.format('%q', x)
"ID"
> =#x
2
> =#string.format('%q', x)
4
Your code does not compile (you need [] around the index), and you should use the raw string of ID, not the "quoted" string:
GET_TABLE = {[1]=ID}
key = string.format("%s", GET_TABLE[1])
Note that I had to initialize ID and RETURN_TABLE objects to the following:
ID = 'ID'
RETURN_TABLE = {}
Stylistic note: you should only use all-caps names for constants, otherwise too many makes code hard to read

How to quickly initialise an associative table in Lua?

In Lua, you can create a table the following way :
local t = { 1, 2, 3, 4, 5 }
However, I want to create an associative table, I have to do it the following way :
local t = {}
t['foo'] = 1
t['bar'] = 2
The following gives an error :
local t = { 'foo' = 1, 'bar' = 2 }
Is there a way to do it similarly to my first code snippet ?
The correct way to write this is either
local t = { foo = 1, bar = 2}
Or, if the keys in your table are not legal identifiers:
local t = { ["one key"] = 1, ["another key"] = 2}
i belive it works a bit better and understandable if you look at it like this
local tablename = {["key"]="value",
["key1"]="value",
...}
finding a result with : tablename.key=value
Tables as dictionaries
Tables can also be used to store information which is not indexed
numerically, or sequentially, as with arrays. These storage types are
sometimes called dictionaries, associative arrays, hashes, or mapping
types. We'll use the term dictionary where an element pair has a key
and a value. The key is used to set and retrieve a value associated
with it. Note that just like arrays we can use the table[key] = value
format to insert elements into the table. A key need not be a number,
it can be a string, or for that matter, nearly any other Lua object
(except for nil or 0/0). Let's construct a table with some key-value
pairs in it:
> t = { apple="green", orange="orange", banana="yellow" }
> for k,v in pairs(t) do print(k,v) end
apple green
orange orange
banana yellow
from : http://lua-users.org/wiki/TablesTutorial
To initialize associative array which has string keys matched by string values, you should use
local petFamilies = {["Bat"]="Cunning",["Bear"]="Tenacity"};
but not
local petFamilies = {["Bat"]=["Cunning"],["Bear"]=["Tenacity"]};

Resources