Lua table.concat - lua

Is there a way to use the arg 2 value of table.concat to represent the current table index?
eg:
t = {}
t[1] = "a"
t[2] = "b"
t[3] = "c"
X = table.concat(t,"\n")
desired output of table concat (X):
"1 a\n2 b\n3 c\n"

Simple answer : no.
table.concat is something really basic, and really fast.
So you should do it in a loop anyhow.
If you want to avoid excessive string concatenation you can do:
function concatIndexed(tab,template)
template = template or '%d %s\n'
local tt = {}
for k,v in ipairs(tab) do
tt[#tt+1]=template:format(k,v)
end
return table.concat(tt)
end
X = concatIndexed(t) -- and optionally specify a certain per item format
Y = concatIndexed(t,'custom format %3d %s\n')

I don't think so: how would you tell it that the separator between keys and values is supposed to be a space, for example?
You can write a general mapping function to do what you'd like:
function map2(t, func)
local out = {}
for k, v in pairs(t) do
out[k] = func(k, v)
end
return out
end
function joinbyspace(k, v)
return k .. ' ' .. v
end
X = table.concat(map2(t, joinbyspace), "\n")

No. But there is a work around:
local n = 0
local function next_line_no()
n = n + 1
return n..' '
end
X = table.concat(t,'\0'):gsub('%f[%Z]',next_line_no):gsub('%z','\n')

function Util_Concat(tab, seperator)
if seperator == nil then return table.concat(tab) end
local buffer = {}
for i, v in ipairs(tab) do
buffer[#buffer + 1] = v
if i < #tab then
buffer[#buffer + 1] = seperator
end
end
return table.concat(buffer)
end
usage tab is where the table input is and seperator be both nil or string (if it nil it act like ordinary table.concat)
print(Util_Concat({"Hello", "World"}, "_"))
--Prints
--Hello_world

Related

(Lua) How do I break a string into entries on a table?

So I want to take an input like R U R' U' and turn it into a table that contains
R
U
R'
U'
I haven't found an example of code that worked. I have tried this solution from codegrepper, and it didn't work. I have not come up with anything else in my head but my general program, which is supposed to take an input like R and find its place in a table. If R is 1, then it will take the value 1 from another table, which will have the r^ as value 1. Then it will do this with the rest and print it when it is done. So if there is an optimization with this that could make it all quicker than I would like to see it. Thanks and goodbye.
function split(str, pat)
local t = {}
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then table.insert(t, cap) end
last_end = e + 1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
then split it with split(var," ")
local myString = "R U R' U'"
local myTable = {}
for e in string.gmatch(myString, "%S+") do
table.insert(myTable, e)
end
Lua Users Wiki
s:gmatch(pattern)
This returns a pattern finding iterator. The iterator will search
through the string passed looking for instances of the pattern you
passed.
First you need to match all the space-separated parts. You can do this using gmatch. Then you can insert these parts as keys in a hash table, the value being the one-indexed index of the occurrence:
local str = "R U R' U'"
local index = 1
local last_occurrence = {}
for match in str:gmatch"%S+" do
last_occurrence[match] = index
index = index + 1
end
Now you can use your "other table" to obtain the value in constant time:
local other_table = {"r^"}
local value = other_table[last_occurrence.R] -- "r^"

How to iterate Lua table from end?

How do I iterate a simple Lua table, that is a sequence, from end?
Example of wanted behavior:
local mytable = {'a', 'b', 'c'}
for i, value in reversedipairs(mytable) do
print(i .. ": " .. value)
end
should output
3: c
2: b
1: a
How to implement here reversedipairs?
Thank you, #Piglet, for useful link.
local function reversedipairsiter(t, i)
i = i - 1
if i ~= 0 then
return i, t[i]
end
end
function reversedipairs(t)
return reversedipairsiter, t, #t + 1
end
Actually, I figured out an easier way may be to
local mytable = {'a', 'b', 'c'}
for i = #mytable, 1, -1 do
value = mytable[i]
print(i .. ": " .. value)
end
Also you can use standard for statement with reversed index:
for i=1, #mytable do
print(mytable[#mytable + 1 - i])
end

Using a coordinate pair as a key in a Lua table

As the title says, I'm trying to use a coordinate pair (x, y) as a key for a table. Here is what I have done so far
local test = {_props = {}}
local mt = {}
local xMax = 5
local yMax = 5
local function coord2index(x, y)
return ((x-1) * xMax) + y
end
mt.__index = function(s, k)
if s._props[coord2index(k[1], k[2])] ~= nil then
return s._props[coord2index(k[1], k[2])]
end
end
mt.__newindex = function(s, k, v)
s._props[coord2index(k[1], k[2])] = v
end
mt.__call = function (t, k)
if type(k) == "table" then print "Table" end
end
setmetatable(test, mt)
test[{1,2}] = 5
print( test[{1,2}])
This is actually working as expected. What I'm really wondering if there is a way to reduce this even more, something like test[1,2] = 5 and print(test[1,1]). There is no technical need for this, it's purely for my further edification into Lua.
I think your method is good. You can also use strings instead of numbers and do something like return x..';'..y in your coord2index function.

Lua: Check if a table can be looped through via ipairs & ipairs starting at 0

I have a nice little Lua table parser which prints out pretty looking lua code and I love it...it works beautifully. There is a slight snag...if I go to print a table or array that has any integer keys it loops through it using pairs (which doesn't mess the code up ironically), but I would rather it use ipairs if possible. So I want to know is it possible to check a table (without physically looking at it) if it can use ipairs to loop through it first else use pairs. Then is there a way to start looping at 0 instead of Lua's default 1?
Lua Table Parser (Base code found on google, changed it to make it print more array friendly)...
function TableParser(name, object, tabs)
local function serializeKeyForTable(k)
if type(k)=="number" then
return ""
end
if string.find(k,"[^A-z_-]") then
return k
end
return k
end
local function serializeKey(k)
if type(k)=="number" then
if k == 0 then
return "\t[" .. k .."] = "
else
return "\t"
end
end
if string.find(k,"[^A-z_-]") then
return "\t" .. k .. " = "
end
return "\t" .. k .. " = "
end
if not tabs then tabs = "" end
local function serialize(name, object, tabs) -- = {
local output = tabs .. (name ~= "" and name .. " = " or "") .. "{" .. "\n"
for k,v in pairs(object) do
if type(v) == "number" then
output = output .. tabs .. serializeKey(k) .. v
elseif type(v) == "string" then
output = output .. tabs .. serializeKey(k) .. string.format("%q",v)
elseif type(v) == "table" then
output = output .. serialize(serializeKeyForTable(k), v, tabs.."\t")
elseif type(v) == "boolean" then
output = output .. tabs .. serializeKey(k) .. tostring(v)
else
output = output .. tabs .. serializeKey(k) .. "\"" .. tostring(v) .. "\""
end
if next(object,k) then
output = output .. ",\n"
end
end
return output .. "\n" .. tabs .. "}"
end
return serialize(name, object, tabs)
end
So I want to know is it possible to check a table (without physically looking at it) if it can use ipairs to loop through it first else use pairs.
Don't check, just do! Use ipairs first and keep track of the largest key that the ipairs iterator returned. Then use pairs to iterate again and ignore all integer keys between 1 and that largest key from ipairs.
If you really want to check whether ipairs will do something, then look at index 1 in the table (rawget( object, 1 ) ~= nil). Checking whether ipairs will cover all elements in the table is not possible without iterating the table.
Then is there a way to start looping at 0 instead of Lua's default 1?
ipairs(t) returns three values: an iterator function, the table t as the state variable, and an initial index value 0. If you use -1 as initial index value, ipairs will start the iteration at 0 (the iterator function always increments by one before using the index value):
t = { 1, 2, 3, [ 0 ] = 0 }
for i,v in ipairs( t ), t, -1 do -- only use first value returned by ipairs
print( i, v )
end
However, be aware that Lua 5.2 has added support for a new metamethod __ipairs which allows you to return a custom iterator triplet to use for ipairs iteration, and the iterator function returned in this case might need different state and initial index values.
Edit:
To incorporate the suggestions into your code insert before the for k,v in pairs(object) do-loop:
local largest = 0
for k,v in ipairs(object) do
largest = k
local t = type(v)
if t == "table" then
output = output .. tabs .. "\t" .. serialize( "", v, tabs.."\t" )
elseif t == "string" then
output = output .. tabs .. "\t" .. string.format("%q", v)
else
output = output .. tabs .. "\t" .. tostring(v)
end
output = output .. ",\n"
end
and inside the loop add an additional if statement to check for array keys:
for k,v in pairs(object) do
if type(k) ~= "number" or k < 1 or k > largest or math.floor(k) ~= k then
-- if type(v) == "number" then
-- ...
end
end
If you apply this modified TableParser function to the following table:
local t = {
1, 2, 3,
value = "x",
tab = {
"a", "b", field = "y"
}
}
print( TableParser( "", t ) )
the output is:
{
1,
2,
3,
tab = {
"a",
"b",
field = "y"
},
value = "x"
}
But doing table serialization properly is tricky. E.g. your implementation doesn't handle cycles or tables as keys. See the Lua Wiki for some implementations.
You can always iterate a table both with pairs and ipairs, whether it makes sense or not.
ipairs iterates over the sequence present in the array (which means sequential integer keys starting with 1, up to the first missing value), unless overridden with metamethod __ipairs (5.2).
pairs iterates over all key-value pairs with next (thus in an unspecified order), unless overridden with metamethod __pairs (5.2).
Which means that ipairs will generally not enumerate any key-value-pair pairs won't show.
And there is no way to verify whether ipairs will enumerate all keys pairs will enumerate, but enumerating everything and testing manually.
BTW: You can make your own iterator which first iterates over the sequence, and then over everything else:
function my_iter(t)
local k, cap
return function()
local v
if k == nil then k, cap = 0 end
if not cap then
k = k + 1
v = t[k]
if v ~= nil then return k, v end
cap, k = k
end
repeat k, v = next(k)
until type(k) ~= "number" or 0 < k and k < cap and math.ceil(k) == k
return k, v
end
end
Though probably better just sort the keys for pretty-printing:
function sorted_iter(t)
local keys, index = {}, 0
for k in next, t do
keys[#keys + 1] = k
end
table.sort(keys)
return function()
index = index + 1
local k = keys[index]
return k, t[k]
end
end

Get a certain value from a concatenated table

Trying to allow a concatenated table to be referenced as such:
local group = table.concat(arguments, ",", 1)
where arguments = {"1,1,1"}
Currently, doing group[2] gives me the comma. How do I avoid that while still allowing for two-digit numbers?
(snippet of what I'm trying to use it for)
for i = 1, #group do
target:SetGroup(i, tonumber(group[i]))
end
Maybe you want something like
local i = 1
for v in string.gmatch(s, "(%w+),*") do
group[i] = v
i = i + 1
end
Revised version in response to comment, avoiding the table altogether:
local i = 1
for v in string.gmatch(s, "(%w+),*") do
target:SetGroup(i, tonumber(v))
i = i + 1
end
split function (you have to add it to code)
split = function(str, delim)
if not delim then
delim = " "
end
-- Eliminate bad cases...
if string.find(str, delim) == nil then
return { str }
end
local result = {}
local pat = "(.-)" .. delim .. "()"
local nb = 0
local lastPos
for part, pos in string.gfind(str, pat) do
nb = nb + 1
result[nb] = part
lastPos = pos
end
-- Handle the last field
result[nb + 1] = string.sub(str, lastPos)
return result
end
so
local arguments = {"1,1,1"};
local group = split(arguments[1], ",");
for i = 1, #group do
target:SetGroup(i, tonumber(group[i]))
end
also note that
local arguments = {"1,1,1"};
local group = split(arguments[1], ",");
local group_count = #group;
for i = 1, group_count do
target:SetGroup(i, tonumber(group[i]))
end
is faster code ;)

Resources