What is the function of square brackets around table keys in lua? - lua

I came across tables that have square brackets around keys:
local commands_json =
{
["request"] = {
["application"] = PW_APPLICATION,
["push_token"] = deviceToken
}
}
Can the square brackets be omitted?

It's simply the long form of specifying keys in a table. You can put any value between the [] (except nil. And floating-point NaNs). Whereas without them, you can only use identifiers.
For example:
tbl =
{
key name = 5,
}
That's a compile error, since "key name" isn't an identifier (due to the space). This works:
tbl =
{
["key name"] = 5,
}
And this:
tbl =
{
"key name" = 5,
}
Is also a compile error. If Lua sees a naked value like this, it thinks you're trying to add to the array part of the table. That is, it confuses it with:
tbl =
{
"key name",
}
Which creates a 1-element array, with tbl[1] equal to "key name". By using [], the compiler can easily tell that you meant for something to be a key rather than the value of an array element.
The long form also lets you distinguish between:
local name = "a name";
tbl =
{
["name"] = 5,
[name] = 7,
}
The second part means to evaluate the expression name, the result of which will be the key. So this table has the keys "name" and "a name".

You cannot omit the brackets
> x = { 'a' = 1 }
stdin:1: '}' expected near '='
the correct code is
> x = { ['a'] = 1 }
> print(x['a'])
1
or
> x = { a = 1 }
> print(x['a'])
1
However, the second one has its limitations. What if you want to have a key called "-"?
> x = { - = 1 }
stdin:1: unexpected symbol near '='
> x = { '-' = 1 }
stdin:1: '}' expected near '='
again the correct way is to use brackets
> x = { ['-'] = 1 }
> print(x['-'])
1
Or you want to create a field of name which is contained in a variable called a?
> a = 'cat'
> x = { [a] = 1 }
> print(x['cat'])
1
Brackets are used as a general form of key creation, they give you ability to put any hashable object as a key - not only strings.

Related

Lua unusual variable name (question mark variable)

I have stumbled upon this line of code and I am not sure what the [ ? ] part represents (my guess is it's a sort of a wildcard but I searched it for a while and couldn't find anything):
['?'] = function() return is_canadian and "eh" or "" end
I understand that RHS is a functional ternary operator. I am curious about the LHS and what it actually is.
Edit: reference (2nd example):
http://lua-users.org/wiki/SwitchStatement
Actually, it is quite simple.
local t = {
a = "aah",
b = "bee",
c = "see",
It maps each letter to a sound pronunciation. Here, a need to be pronounced aah and b need to be pronounced bee and so on. Some letters have a different pronunciation if in american english or canadian english. So not every letter can be mapped to a single sound.
z = function() return is_canadian and "zed" or "zee" end,
['?'] = function() return is_canadian and "eh" or "" end
In the mapping, the letter z and the letter ? have a different prononciation in american english or canadian english. When the program will try to get the prononciation of '?', it will calls a function to check whether the user want to use canadian english or another english and the function will returns either zed or zee.
Finally, the 2 following notations have the same meaning:
local t1 = {
a = "aah",
b = "bee",
["?"] = "bee"
}
local t2 = {
["a"] = "aah",
["b"] = "bee",
["?"] = "bee"
}
If you look closely at the code linked in the question, you'll see that this line is part of a table constructor (the part inside {}). It is not a full statement on its own. As mentioned in the comments, it would be a syntax error outside of a table constructor. ['?'] is simply a string key.
The other posts alreay explained what that code does, so let me explain why it needs to be written that way.
['?'] = function() return is_canadian and "eh" or "" end is embedded in {}
It is part of a table constructor and assigns a function value to the string key '?'
local tbl = {a = 1} is syntactic sugar for local tbl = {['a'] = 1} or
local tbl = {}
tbl['a'] = 1
String keys that allow that convenient syntax must follow Lua's lexical conventions and hence may only contain letters, digits and underscore. They must not start with a digit.
So local a = {? = 1} is not possible. It will cause a syntax error unexpected symbol near '?' Therefor you have to explicitly provide a string value in square brackets as in local a = {['?'] = 1}
they gave each table element its own line
local a = {
1,
2,
3
}
This greatly improves readability for long table elements or very long tables and allows you maintain a maximum line length.
You'll agree that
local tbl = {
z = function() return is_canadian and "zed" or "zee" end,
['?'] = function() return is_canadian and "eh" or "" end
}
looks a lot cleaner than
local tbl = {z = function() return is_canadian and "zed" or "zee" end,['?'] = function() return is_canadian and "eh" or "" end}

Lua - iterating through nested list

I'm going to simplify the situation as much as I can. I have the following code:
windows = { "window1", "window2" }
window1 = {
x = 100
y = 100
properties = { active = false, width = 200, height = 200 }
}
window2 = {
x = 0
y = 0
properties = { active = false, width = 200, height = 200 }
}
If I do the following, I get the correct output:
print (window1.x)
OUTPUT: 0
print (window1.properties.active)
OUTPUT: false
HOWEVER, if I iterate through the list, I get "nil" values for "l.x" and "l.properties.active":
for _,l in ipairs(windows) do
print (l)
print (l.x)
print (l.properties.active)
end
Is there a different way I need to iterate through the variables in the lists, so I can get the values?
That is not a nested table, but just a table containing strings. And, as you just saw, a string doesn't contain a value for the key "x".
You have to put the tables in a sequence:
local window1 = {...} -- first table
local window2 = {...} -- second table
local windows = {window1, window2}
for _,l in ipairs(windows) do
-- do stuff with l
end
Or, if you want to keep the list of strings and iterate over the strings, put the windows in a second table using these strings as a key.
local windowNames = { "window1", "window2" }
local windows = {}
windows.window1 = {...} -- first table
windows.window2 = {...} -- second table
for _,l in ipairs(windowNames) do
local ourWindow = windows[l]
-- do stuff with ourWindow
end

LPeg Increment for Each Match

I'm making a serialization library for Lua, and I'm using LPeg to parse the string. I've got K/V pairs working (with the key explicitly named), but now I'm going to add auto-indexing.
It'll work like so:
#"value"
#"value2"
Will evaluate to
{
[1] = "value"
[2] = "value2"
}
I've already got the value matching working (strings, tables, numbers, and Booleans all work perfectly), so I don't need help with that; what I'm looking for is the indexing. For each match of #[value pattern], it should capture the number of #[value pattern]'s found - in other words, I can match a sequence of values ("#"value1" #"value2") but I don't know how to assign them indexes according to the number of matches. If that's not clear enough, just comment and I'll attempt to explain it better.
Here's something of what my current pattern looks like (using compressed notation):
local process = {} -- Process a captured value
process.number = tonumber
process.string = function(s) return s:sub(2, -2) end -- Strip of opening and closing tags
process.boolean = function(s) if s == "true" then return true else return false end
number = [decimal number, scientific notation] / process.number
string = [double or single quoted string, supports escaped quotation characters] / process.string
boolean = P("true") + "false" / process.boolean
table = [balanced brackets] / [parse the table]
type = number + string + boolean + table
at_notation = (P("#") * whitespace * type) / [creates a table that includes the key and value]
As you can see in the last line of code, I've got a function that does this:
k,v matched in the pattern
-- turns into --
{k, v}
-- which is then added into an "entry table" (I loop through it and add it into the return table)
Based on what you've described so far, you should be able to accomplish this using a simple capture and table capture.
Here's a simplified example I knocked up to illustrate:
lpeg = require 'lpeg'
l = lpeg.locale(lpeg)
whitesp = l.space ^ 0
bool_val = (l.P "true" + "false") / function (s) return s == "true" end
num_val = l.digit ^ 1 / tonumber
string_val = '"' * l.C(l.alnum ^ 1) * '"'
val = bool_val + num_val + string_val
at_notation = l.Ct( (l.P "#" * whitesp * val * whitesp) ^ 0 )
local testdata = [[
#"value1"
#42
# "value2"
#true
]]
local res = l.match(at_notation, testdata)
The match returns a table containing the contents:
{
[1] = "value1",
[2] = 42,
[3] = "value2",
[4] = true
}

How to reference lua table member from table member?

i have a table in lua:
enUS = {
LOCALE_STHOUSANDS = ",", --Thousands separator e.g. comma
patNumber = "%d+["..LOCALE_STHOUSANDS.."%d]*", --regex to find a number
["PreScanPatterns"] = {
["^("..patNumber..") Armor$"] = "ARMOR",
}
}
So you see there is a whole chain of self-references in this table:
LOCAL_STHOUSANDS
patNumber
["^("..patNumber..") Armor$"]
How can i perform self-referencing in an lua table?
What i don't want to do is have to hard-replace the values; there are hundreds of references:
enUS = {
LOCALE_STHOUSANDS = ",", --Thousands separator e.g. comma
patNumber = "%d+[,%d]*", --regex to find a number
["PreScanPatterns"] = {
["^(%d+[,%d]*) Armor$"] = "ARMOR",
}
}
How can i perform self-referencing in an lua table?
You don't.
Lua is not C. Until the table is constructed, none of the table entries exist. Because the table itself doesn't exist yet. Therefore, you can't have one entry in a table constructor reference another entry in a table that doesn't exist.
If you want to cut down on repeated typing, then you should use local variables and do/end blocks:
do
local temp_thousands_separator = ","
local temp_number_pattern = "%d+["..LOCALE_STHOUSANDS.."%d]*"
enUS = {
LOCALE_STHOUSANDS = temp_thousands_separator, --Thousands separator e.g. comma
patNumber = "%d+["..temp_thousands_separator.."%d]*", --regex to find a number
["PreScanPatterns"] = {
["^("..temp_number_pattern..") Armor$"] = "ARMOR",
}
}
end
The do/end block is there so that the temporary variables don't exist outside of the table creation code.
Alternatively, you can do the construction in stages:
enUS = {}
enUS.LOCALE_STHOUSANDS = ",", --Thousands separator e.g. comma
enUS.patNumber = "%d+["..enUS.LOCALE_STHOUSANDS.."%d]*", --regex to find a number
enUS["PreScanPatterns"] = {
["^("..enUS.patNumber..") Armor$"] = "ARMOR",
}
There's no way of doing this inside the constructor itself, but you can do it after creating the table like so:
enUS = {
LOCALE_STHOUSANDS = ","
}
enUS.patNumber = "%d+["..enUS.LOCALE_STHOUSANDS.."%d]*"
enUS.PreScanPatterns = {
["^("..enUS.patNumber..") Armor$"] = "ARMOR",
}
If you specifically need to refer to the current table, Lua provides a "self" parameter, but it's only accessible in functions.
local t = {
x = 1,
y = function(self) return self.x end
}
-- this is functionally identical to t.y
function t:z() return self.x end
-- these are identical and interchangeable
print(t:y(), t.z(t))
-- 1, 1

Using table keys with periods in Lua

In Lua, assigning a table with a specified key might go like this:
a = { x = 4 }
...or perhaps like...
a = { ['x'] = 4 }
Easy enough. However, if I introduce periods into the key (as in a domain name) nothing seems to work. All of the following fail:
a = { "a.b.c" = 4 }
a = { a.b.c = 4 }
a = { ['a.b.c'] = 4 }
a = { ["a.b.c"] = 4 }
a = { [a.b.c] = 4 }
All of these return the same error:
$ ./script.lua
/usr/bin/lua: ./script.lua:49: `}' expected near `='
What am I missing here? Several of the examples seem quite straight-forward and should work (while others have apparent problems).
In lua table element may be either a Name or an Expression. Citing language reference, "Names (also called identifiers) in Lua can be any string of letters, digits, and underscores, not beginning with a digit.", and everything else is interpreted as an identifier in this context. Therefore, a.b.c as a table index is treated as expression, which is evaluated to get the actual table index. This would work, but would be useless:
a = { b = { c = 1 } }
x = {}
x['a.b.c'] = 7
print(x['a.b.c'])
Also note, that foo.a.b.c is equal to foo['a']['b']['c'] and not to foo['a.b.c'].
a = { ['a.b.c'] = 4 }
a = { ["a.b.c"] = 4 }
These two are all valid.
a = { [a.b.c] = 4 }
This could be valid, depending on the exact identifiers used. For example
b = { c = { d = "Ohai!" } } }
a = { [b.c.d] = 4 }
would be valid.
If your interpreter is telling you they are not valid, then you've either done something else wrong, or there's a bug in the interpreter. The others however would not be valid.
Is there something else wrong in your script?
$ ./lua
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> a = { ["a.b.c"] = 4 }
> print (a["a.b.c"])
4
> print (a.a)
nil

Resources