I have this code written in Lua, it's just an example code, because the actual one I'm using is bigger than this, but this is the part I'm having problems.
Does anyone know why when I try to print what's inside the table t I get nil as result?
t = {
{name="John",sex="M",age=19},
{name="Susan",sex="F",age=20}
}
for _ in ipairs(t) do
print("NAME: " .. t.name)
print("SEX: " .. t.sex)
print("AGE: " .. t.age)
print("\n")
end
I mean, this is the result I get when I run the code:
attempt to concatenate field 'name' (a nil value)
Iterating over t doesn't change t. You need to specify where to put the values you are iterating over, and use those variables.
local t = {
{name="John",sex="M",age=19},
{name="Susan",sex="F",age=20}
}
for index, value in ipairs(t) do
print("NAME: " .. value.name)
print("SEX: " .. value.sex)
print("AGE: " .. value.age)
print("\n")
end
Related
In Lua 4, many tables have an "n" property which tracks the number of items inside the table.
Do all tables have this property?
Can it be overridden?
I ask, because I'm trying to develop a routine that prints all of a table's elements recursively in valid Lua syntax, and want to know if it's safe to filter all "n" items out of the result?
Thanks.
[edit]
Here's the script:
-- ThoughtDump v1.4.0
-- Updated: 2017/07/25
-- *****************
-- Created by Thought (http://hw2.tproc.org)
-- Updated by Mikali
-- DESCRIPTION
-- ***********
-- Parses the globals table and __TDPrints its contents to "HW2.log".
-- Can also be used to parse (i.e., pretty-print) generic tables in some cases.
-- Note: functions & variables must actually be declared in order to be parsed.
-- Otherwise, they are ignored.
-- Note: if parsing a table other than the globals table, the __TDPrinted table
-- values may be in a different order than was originally written. Values with
-- numerical indices are moved to the "top" of the table, followed by values
-- with string indices, followed by tables. Functions appear in different
-- locations, depending on whether they are indexed using a number or a string.
-- Note: despite the fact that nil values cannot be stored in tables, they are
-- still handled.
-- Note: even though functions may be referenced within tables, a function will
-- only be parsed correctly if it is indexed using a string that is the same as
-- the name of the function.
__TDOutputString = ""
function __TDParse(name, value, level, verbose, numbers, collapse)
if ((name == "__TDParse") or (name == "__TDSortHash") or (name == "__TDPrint") or (name == "__TDPrintGlobals()") or (name == "__TDOutputString")) then
return
end
local Element = nil
local ValType = type(value)
local NamType = type(name)
local PreLevel = ""
if (collapse == 0) then
for i = 1, level do
PreLevel = PreLevel .. "\t"
end
end
local ComLevel = ""
if (level ~= 0) then
ComLevel = ","
end
if ((ValType == "function") or (ValType == "userdata")) then
if (NamType == "string") then
Element = PreLevel .. name .. " = " .. name .. ComLevel
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] = " .. name .. ComLevel
else
Element = PreLevel .. name .. ComLevel
end
elseif (ValType == "string") then
if (NamType == "string") then
Element = PreLevel .. name .. " = \"" .. value .. "\"" .. ComLevel
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] = \"" .. value .. "\"" .. ComLevel
else
Element = PreLevel .. "\"" .. value .. "\"" .. ComLevel
end
elseif (ValType == "number") then
if (NamType == "string") then
Element = PreLevel .. name .. " = " .. value .. ComLevel
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] = " .. value .. ComLevel
else
Element = PreLevel .. value .. ComLevel
end
elseif (ValType == "table") then
if (NamType == "string") then
Element = PreLevel .. name .. " ="
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] ="
else
Element = ""
end
elseif (ValType == "nil") then
if (NamType == "string") then
Element = PreLevel .. name .. " = nil" .. ComLevel
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] = nil" .. ComLevel
else
Element = PreLevel .. "nil" .. ComLevel
end
else
Element = PreLevel .. "-- unknown object type " .. ValType .. " for object " .. name
end
if (verbose == 1) then
Element = Element .. " -- " .. ValType .. ", tag: " .. tag(value)
end
if (((ValType == "table") and (NamType == "number") and (numbers == 0)) or (collapse == 1)) then
__TDPrint(Element, 0)
else
__TDPrint(Element, 1)
end
if (ValType == "table") then
__TDPrint(PreLevel .. "{", collapse == 0)
__TDSortHash(__TDParse, value, level + 1, verbose, numbers, collapse)
__TDPrint(PreLevel .. "}" .. ComLevel, 1)
end
end
function __TDSortHash(func, tabl, level, verbose, numbers, collapse)
local typesarray = {}
local typescount = {}
local keycount = 1
local keyarray = {}
for i, iCount in tabl do
local thistype = type(iCount)
if not (typesarray[thistype]) then
typescount[thistype] = 0
typesarray[thistype] = {}
end
typescount[thistype] = typescount[thistype] + 1
typesarray[thistype][typescount[thistype]] = i
end
sort(typesarray)
for i, iCount in typesarray do
sort(iCount)
for j, jCount in iCount do
keyarray[keycount] = tostring(jCount)
keycount = keycount + 1
end
end
for i, iCount in keyarray do
local tempcount = tonumber(iCount)
if (tempcount) then
iCount = tempcount
end
func(iCount, tabl[iCount], level, verbose, numbers, collapse)
end
end
function __TDPrint(instring, newline)
__TDOutputString = __TDOutputString .. instring
if (newline == 1) then
__TDOutputString = __TDOutputString .. "\n"
end
end
function __TDPrintGlobals()
__TDOutputString = ""
__TDPrint("globals =", 1)
__TDPrint("{", 1)
__TDSortHash(__TDParse, globals(), 1, 0, 0, 0)
__TDPrint("}\n", 1)
local WriteFile = "$test_globals_write.lua"
writeto(WriteFile)
write(__TDOutputString)
writeto()
end
__TDPrintGlobals()
In Lua 4.x in tables, n is just an element of the table like any other elements a table could contain, but it's not part of the table mechanism itself.
So, it can be overwritten or removed.
Some functions use it, like tinsert() ,and other table functions:
local tbl = { n=0 }
tinsert(tbl, 123)
print(tbl.n) --> 1
This is very useful as the getn() function gives only the highest number index of the table. But if there is only named elements in the table or a mix or number indexes and named indexes, then getn() doesn't reflect the real number of elements in the table. If elements are always inserted (or removed) using table functions like tinsert() then n is the accurate number of elements in the table.
Lua 4.x --> Lua 5.x equivalent:
getn(tbl) #tbl
tinsert(tbl,e) table.insert(tbl,e) or tbl:insert(e)
Of course you can still add elements in a table using the simple table access. But as n can be very useful, try to keep it updated as well.
tbl["Bla"] = 234
tbl.Bli = 345
tbl.n = tbl.n + 2
If n doesn't exist in a table but is needed by the code somewhere, it can be added using the for loop:
local tbl = {1,2,3,4,5,6}; tbl.a=11; tbl.b=22; tbl.c=33
local n = 0
for ie, e in tbl do
n = n + 1
end
tbl.n = n
or the foreach loop:
local tbl = {1,2,3,4,5,6}; tbl.a=11; tbl.b=22; tbl.c=33
tbl.n = 0
foreach(tbl, function() %tbl.n = %tbl.n + 1 end )
Note 1: The initialization of tbl.n to 0 will give the number of elements in the table, including n. Here the result of tbl.n is 10.
As eventually we don't want n to be counted as a real element of the table (which is is) but only counting other elements, we should init n to -1 then.
Note 2: The Lua 4.x upvalue operator % is used here because the tbl variable is not reachable in the function (not in the scope) for the foreach loop. It can be reached using %tbl. However, a upvalue is always read only, so the tbl variable can't be changed. The following will generate an error in the function:
%tbl = { } -- change the reference to another table
%tbl = 135 -- change the ref to the table for a number (or a string, ...)
As tbl variable contains in fact a reference to a table, the referenced table can be modifiable and therefore the element n can be changed without problem (as well as other elements of the table).
%tbl.n = %tbl.n + 1 -- increment the element n of the referenced table
Note 3: A global variable tbl could have been used, but it's good practice to always use local variable. Access to local variables is also faster than global.
Not all tables have this property.
It can be overwritten.
Why not traverse the table using a for loop? Or if possible, use Lua 5.3 ;)
In Lua this was called table for loop, in modern Lua it's called generic for loop.
The table for statement traverses all pairs (index,value) of a given
table. It has the following syntax:
stat ::= for name `,' name in exp1 do block end
A for statement like
for index, value in exp do block end
is equivalent to the code:
do
local _t = exp
local index, value = next(t, nil)
while index do
block
index, value = next(t, index)
end
end
Note the following:
_t is an invisible variable. The name is here for explanatory purposes only.
The behavior is undefined if you assign to index inside the block.
The behavior is undefined if you change the table _t during the traversal.
The variables index and value are local to the statement; you cannot use their values after the for ends.
You can use break to exit a for. If you need the value of index or value, assign them to other variables before breaking.
The order that table elements are traversed is undefined, even for numerical indices. If you want to traverse indices in numerical order,
use a numerical for.
Refer to the Lua manual 4.4.4
https://www.lua.org/manual/4.0/manual.html#4.4
I am trying to write a little script to output filetags in mpv. My script looks like this:
require 'os'
require 'string'
function displayTrack()
currentTrack = mp.get_property("metadata/by-key/Title")
currentArtist = mp.get_property("metadata/by-key/Artist")
currentAlbum = mp.get_property("metadata/by-key/Album")
print(currentArtist)
print(currentAlbum)
print(currentTrack)
if currentTrack == nil then
os.execute("terminal-notifier -title '" .. currentArtist .. "' -message 'Unknown Title'")
else
os.execute("terminal-notifier -title '" .. currentArtist .. "' -message '" .. currentAlbum .. " - " .. currentTrack .. "'")
end
end
mp.observe_property("eof-reached", "bool", displayTrack)
Catching the tags and printing them works with every tested title. But if i want to uncomment the 5 lines starting with "if currentTrack == nil ..." so it also dislpays a native notification i get the LUA error:
/Users/marcel/.config/mpv/scripts/notification.lua:15: attempt to concatenate global 'currentArtist' (a nil value)
Can somebody tell me why i can print the string but not forward it to the os.execute?
It is not os.execute, it is concatenation - .. - that can't work with nil. And yes, you can print standalone nil just fine. In your case not only currentTrack is nil, but currentArtist too, so you can't build a string with it. Consider if you even need those entries where you don't have value for currentArtist and either skip them, provide alternative if branch to do something else or provide some default in concatenation. Usual idiom is (currentArtist or '') - here your default will be empty string.
if currentTrack == nil then
os.execute("terminal-notifier -title '" .. currentArtist .. "' -message 'Unknown Title'")
If this branch gets executed, currentTrack is nil, thus the concatenation fails as stated by the error message.
Just get rid of the concatenation all together:
if currentTrack == nil then
os.execute("terminal-notifier -title -message 'Unknown Title'")
Good evening
Will you help me solve this problem?
ERROR: race/util_server.lua:440: attempt to index local 'self' (a nil value)
function string:split(separator)
if separator == '.' then
separator = '%.'
end
local result = {}
for part in self:gmatch('(.-)' .. separator) do
result[#result+1] = part
end
result[#result+1] = self:match('.*' .. separator .. '(.*)$') or self
return result
end
You're probably calling it wrong.
function string:split(separator)
Is short hand for:
function string.split(self, separator)
Given a string and separator:
s = 'This is a test'
separator = ' '
You need to call it like this:
string.split(s, separator)
Or:
s:split(separator)
If you call it like this:
s.split(separator)
You're failing to provide a value for the self argument.
Side note, you can write split more simply like this:
function string:split(separators)
local result = {}
for part in self:gmatch('[^'..separators..']+') do
result[#result + 1] = part
end
return result
end
This has the disadvantage that you can't used multi-character strings as delimiters, but the advantage that you can specify more than one delimiter. For instance, you could strip all the punctuation from a sentence and grab just the words:
s = 'This is an example: homemade, freshly-baked pies are delicious!'
for _,word in pairs(s:split(' :.,!-')) do
print(word)
end
Output:
This
is
an
example
homemade
freshly
baked
pies
are
delicious
I'm trying to use in pairs inside a function but it doesn't work, it just prints the first row (key). This is my code:
set = {1,2,3,4};
unset = {5,6,7,8};
function listen(ftype)
if (ftype == [[~]]) then
for num,str in pairs(set) do
return str;
end
end
if (ftype == [[$]]) then
for num,str in pairs(unset) do
return str;
end
end
end
print(listen([[~]])..[[ =:= ]]..listen([[$]]));
If I do something like this..
for num,str in pairs(unset) do
print(str);
end
It works like a charm. That’s exactly what I want but inside a function.
You can build your own iterator:
function double_pair(t1, t2)
local i = 0
return function() i = i + 1
return t1[i] and t1[i] .. " =:= " .. t2[i]
end
end
Then you can use it like this:
for str in double_pair(set, unset) do
print(str)
end
Output:
1 =:= 5
2 =:= 6
3 =:= 7
4 =:= 8
Note that you don't need semicolons to end your statement, unless the statements are in one line and you want to make them clear. And [[ =:= ]] is usually used to build long multi-line strings, normally we choose to use double quote " =:= " or single quote ' =:= '.
A function can't return multiple times. Putting an unconditional return inside a loop is nonsensical - it will never get to the second iteration of the loop.
You are essentially trying to return multiple values from the function. Lua supports that; you could, for instance, just return 1,2,3,4. For an unknown number of return values, you can build them up in a table and call unpack on it, like so:
function listen(ftype)
local result = {}
local num, str
if (ftype == [[~]]) then
for num,str in pairs(set) do
table.insert(result, str)
end
elseif (ftype == [[$]]) then
for num,str in pairs(unset) do
table.insert(result, str)
end
end
return unpack(result)
end
But since your results are already in a couple tables, it would be silly to reconstruct them that way. You can just unpack the originals:
function listen(ftype)
if (ftype == [[~]]) then
return unpack(set)
elseif (ftype == [[$]]) then
return unpack(unset)
end
end
Great. But when you put the function call into an expression like your print statement, it will only return the first value, which puts you back where you started.
So to print out your pairs, you can't avoid having to either:
1) do some iteration outside the function
or
2) do the actual printing inside the function
The cleanest solution is probably a custom iterator, as suggested by #YuHao.
I have the following function in Lua:
function iffunc(k,str,str1)
if k ~= 0 then
return str .. k .. (str1 or "")
end
end
This function allows me to check if value k is populated or not. I'm actually using it to determine if I want to display something that has zero value. My problem is this: I'm trying to concatenate a string of iffunc(), but since some of the values are 0, it returns an error of trying to concatenate a nil value. For instance:
levellbon = iffunc(levellrep["BonusStr"],"#wStr#r{#x111","#r}") .. iffunc(levellrep["BonusInt"],"#wInt#r{#x111","#r}") .. iffunc(levellrep["BonusWis"],"#wWis#r{#x111","#r}")
If any of the table values are 0, it'll return the error. I could easily put 'return 0' in the iffunc itself; however, I don't want a string of 000, either. So how can I work it where no matter which values are nil, I won't get that error? Ultimately, I'm going to do an iffunc on the levellbon variable to see if that's populated or not, but I've got that part figured out. I just need to get past this little hurdle right now. Thanks!
I'd do this:
function iffunc(k,str,str1)
if k == 0 then return "" end
return str .. k .. (str1 or "")
end
You should add an else statement in the function, where you return an empty string ("").