How to convert string into a table in Lua - lua

I am having a table data in string form. Sample is given below:
{"engName1":"HOLDER","validDurPeriod":3,"engName2":"INFORMATION","appStatus":2,"stayExpDate":"01/10/2012","engName3":"","appExpDate":"12/04/2010"}
How can I convert it into a proper table type variable so that I can access keys.I am new to lua and I am not aware if there is any existing method to do so.

There is plenty of JSON parsers available for Lua, for example dkjson:
local json = require ("dkjson")
local str = [[
{
"numbers": [ 2, 3, -20.23e+2, -4 ],
"currency": "\u20AC"
}
]]
local obj, pos, err = json.decode (str, 1, nil)
if err then
print ("Error:", err)
else
print ("currency", obj.currency)
for i = 1,#obj.numbers do
print (i, obj.numbers[i])
end
end
Output:
currency €
1 2
2 3
3 -2023
4 -4

Try this code to start with
J=[[
{"engName1":"HOLDER","validDurPeriod":3,"engName2":"INFORMATION","appStatus":2,"stayExpDate":"01/10/2012","engName3":"","appExpDate":"12/04/2010"}
]]
J=J:gsub("}",",}")
L={}
for k,v in J:gmatch('"(.-)":(.-),') do
L[k]=v
print(k,v)
end
You'll still need to convert some values to number and remove quotes.
Alternatively, you can let Lua do the hard work, if you trust the source string. Just replace the loop by this:
J=J:gsub('(".-"):(.-),','[%1]=%2,\n')
L=loadstring("return "..J)()

Related

Can't set the data to a comboBox choice

it has been days I'm trying to create a comboBox with all available models.
The code is this
for name , models in SortedPairs( player_manager.AllValidModels() ) do
print("name: "..name.." model: "..models)
custCbox:AddChoice(name , models , false)
end
I tried to print every name and model to know if I was wrong, but that's ok: name prints the display name and models prints the path.
The OnSelect function is this:
custCbox.OnSelect = function( index, value, data )
modelPanel:SetModel( data )
print("Data " .. data)
print("Value " .. value)
end
Data gives the display name and value gives a number.
Why?
From the DComboBox documentation:
local cbox = vgui.Create( "DComboBox", BGPanel )
...
cbox:AddChoice( "Pink", Color( 255, 0, 255 ) )
function cbox:OnSelect( index, text, data )
-- Set background panel color
BGPanel:SetBackgroundColor( data )
end
Note the difference to your
custCbox.OnSelect = function( index, value, data )
modelPanel:SetModel( data )
print("Data " .. data)
print("Value " .. value)
end
which is equivalent to
function custCbox.onSelect(index, value, data)
-- ...
end
And the example that uses the colon syntax.
function cbox:OnSelect(index, text, data)
end
is equivalent to
function cbox.OnSelect(self, index, text, data)
end
This difference causes a parameter shift by one. So what you think is index is actually your combobox, text is the selected index and data is the choices text.
Please read
Lua 5.4 Reference Manual: 3.4.10 Function Calls
Lua 5.4 Reference Manual: 3.4.11 Function Definitions
This is a very common mistake among beginners.

Generating all combinations from a table in Lua

I'm trying to iterate through a table with a variable amount of elements and get all possible combinations, only using every element one time. I've landed on the solution below.
arr = {"a","b","c","d","e","f"}
function tablelen(table)
local count = 0
for _ in pairs(table) do
count = count + 1
end
return count
end
function spellsub(table,start,offset)
local str = table[start]
for i = start+offset, (tablelen(table)+1)-(start+offset) do
str = str..","..table[i+1]
end
return str
end
print(spellsub(arr,1,2)) -- Outputs: "a,d,e" correctly
print(spellsub(arr,2,2)) -- Outputs: "b" supposed to be "b,e,f"
I'm still missing some further functions, but I'm getting stuck with my current code. What is it that I'm missing? It prints correctly the first time but not the second?
A solution with a coroutine iterator called recursively:
local wrap, yield = coroutine.wrap, coroutine.yield
-- This function clones the array t and appends the item new to it.
local function append (t, new)
local clone = {}
for _, item in ipairs (t) do
clone [#clone + 1] = item
end
clone [#clone + 1] = new
return clone
end
--[[
Yields combinations of non-repeating items of tbl.
tbl is the source of items,
sub is a combination of items that all yielded combination ought to contain,
min it the minimum key of items that can be added to yielded combinations.
--]]
local function unique_combinations (tbl, sub, min)
sub = sub or {}
min = min or 1
return wrap (function ()
if #sub > 0 then
yield (sub) -- yield short combination.
end
if #sub < #tbl then
for i = min, #tbl do -- iterate over longer combinations.
for combo in unique_combinations (tbl, append (sub, tbl [i]), i + 1) do
yield (combo)
end
end
end
end)
end
for combo in unique_combinations {'a', 'b', 'c', 'd', 'e', 'f'} do
print (table.concat (combo, ', '))
end
For a tables with consecutive integer keys starting at 1 like yours you can simply use the length operator #. Your tablelen function is superfluous.
Using table as a local variable name shadows Lua's table library. I suggest you use tbl or some other name that does not prevent you from using table's methods.
The issue with your code can be solved by printing some values for debugging:
local arr = {"a","b","c","d","e","f"}
function spellsub(tbl,start,offset)
local str = tbl[start]
print("first str:", str)
print(string.format("loop from %d to %d", start+offset, #tbl+1-(start+offset)))
for i = start+offset, (#tbl+1)-(start+offset) do
print(string.format("tbl[%d]: %s", i+1, tbl[i+1]))
str = str..","..tbl[i+1]
end
return str
end
print(spellsub(arr,1,2)) -- Outputs: "a,d,e" correctly
print(spellsub(arr,2,2)) -- Outputs: "b" supposed to be "b,e,f"
prints:
first str: a
loop from 3 to 4
tbl[4]: d
tbl[5]: e
a,d,e
first str: b
loop from 4 to 3
b
As you see your second loop does not ran as the start value is already greater than the limit value. Hence you only print the first value b
I don't understand how your code is related to what you want to achieve so I'll leave it up to you to fix it.

Use variables inside a string in lua

In javascript we can do the following:
var someString = `some ${myVar} string`
I have the following lua code, myVar is a number that needs to be in the square brackets:
splash:evaljs('document.querySelectorAll("a[title*=further]")[myVar]')
Function that fits you description is string.format:
splash:evaljs(string.format('document.querySelectorAll("a[title*=further]")[%s]', myVar))
It is not as verbose as ${}. It is more of a good old (and hated) sprintf.
I have written an emulation of Python's f'' strings, which is a set of functions you can hide inside a require file. So, if you like Python's f'' strings, this may be what you're looking for.
(If anyone finds errors, please notify.)
It's quite big compared to the other solution, but if you hide the bulk in a library, then its use is more compact and readable, IMO.
With this library you can do the following, for example:
require 'f_strings'
a = 12345
print(f'Number: {a}, formatted with two decimals: {a::%.2f}')
-- Number: 12345, formatted with two decimals: 12345.00
Note the use of Lua string.format formatting codes, and the use of double colon (instead of Python's single colon) for format specifiers because of Lua's use of colon for methods.
I have extracted only the relevant functions from a larger library. Although some optimizations may be possible for this specific use case, I leave them unchanged as they are general purpose and may also be useful for other purposes.
And here's the required library (placed somewhere in your Lua libraries folder):
-- f_strings.lua ---
unpack = table.unpack or unpack
--------------------------------------------------------------------------------
-- Escape special pattern characters in string to be treated as simple characters
--------------------------------------------------------------------------------
local
function escape_magic(s)
local MAGIC_CHARS_SET = '[()%%.[^$%]*+%-?]'
if s == nil then return end
return (s:gsub(MAGIC_CHARS_SET,'%%%1'))
end
--------------------------------------------------------------------------------
-- Returns iterator to split string on given delimiter (multi-space by default)
--------------------------------------------------------------------------------
function string:gsplit(delimiter)
if delimiter == nil then return self:gmatch '%S+' end --default delimiter is any number of spaces
if delimiter == '' then return self:gmatch '.' end
if type(delimiter) == 'number' then --break string in equal-size chunks
local index = 1
local ans
return function()
ans = self:sub(index,index+delimiter-1)
if ans ~= '' then
index = index + delimiter
return ans
end
end
end
if self:sub(-#delimiter) ~= delimiter then self = self .. delimiter end
return self:gmatch('(.-)'..escape_magic(delimiter))
end
--------------------------------------------------------------------------------
-- Split a string on the given delimiter (comma by default)
--------------------------------------------------------------------------------
function string:split(delimiter,tabled)
tabled = tabled or false --default is unpacked
local ans = {}
for item in self:gsplit(delimiter) do
ans[#ans+1] = item
end
if tabled then return ans end
return unpack(ans)
end
--------------------------------------------------------------------------------
function copy(t) --returns a simple (shallow) copy of the table
if type(t) == 'table' then
local ans = {}
for k,v in next,t do ans[ k ] = v end
return ans
end
return t
end
--------------------------------------------------------------------------------
function eval(expr,vars)
--evaluate a string expression with optional variables
if expr == nil then return end
vars = vars or {}
assert(type(expr) == 'string','String expected as 1st arg')
assert(type(vars) == 'table','Variable table expected as 2nd arg')
local env = {abs=math.abs,acos=math.acos,asin=math.asin,atan=math.atan,
atan2=math.atan2,ceil=math.ceil,cos=math.cos,cosh=math.cosh,
deg=math.deg,exp=math.exp,floor=math.floor,fmod=math.fmod,
frexp=math.frexp,huge=math.huge,ldexp=math.ldexp,log=math.log,
max=math.max,min=math.min,modf=math.modf,pi=math.pi,pow=math.pow,
rad=math.rad,random=math.random,randomseed=math.randomseed,
sin=math.sin,sinh=math.sinh,sqrt=math.sqrt,tan=math.tan,
tanh=math.tanh}
for name,value in pairs(vars) do env[name] = value end
local a,b = pcall(load('return '..expr,nil,'t',env))
if a == false then return nil,b else return b end
end
--------------------------------------------------------------------------------
-- f'' formatted strings like those introduced in Python v3.6
-- However, you must use Lua style format modifiers as with string.format()
--------------------------------------------------------------------------------
function f(s)
local env = copy(_ENV) --start with all globals
local i,k,v,fmt = 0
repeat
i = i + 1
k,v = debug.getlocal(2,i) --two levels up (1 level is this repeat block)
if k ~= nil then env[k] = v end
until k == nil
local
function go(s)
local fmt
s,fmt = s:sub(2,-2):split('::')
if s:match '%b{}' then s = (s:gsub('%b{}',go)) end
s = eval(s,env)
if fmt ~= nil then
if fmt:match '%b{}' then fmt = eval(fmt:sub(2,-2),env) end
s = fmt:format(s)
end
return s
end
return (s:gsub('%b{}',go))
end

How to make encode and decode functions as I want?

--encode
function strToBytes(str)
local bytes = { str:byte(1, -1)
for i = 1, #bytes do
bytes[i] = bytes[i] + 100
end
return table.concat(bytes, ',')
end
--decode
function bytesToStr(str)
local function gsub(c)return string.char(c - 100) end
return str:gsub('(%d+),?', gsub) end
implemented :
str = "hello world"
strbyte = strToBytes(str)
bytestr = bytesToStr(strbyte)
print(strbyte)
Output :
204,201,208,208,211,132,219,211,214,208,200
print(bytestr)
Output :
"Hello world"
Hi, I need improving my code above. Actually encode and decode functions is work fine, but I need a little bit change.
I want to make encode functions similar like code above, but, the results is table like below :
{204,201,208,208,211,132,219,211,214,208,200}
Then, same as like my first decode functions, all bytes inside the table should be back to "hello world".
I hope my purpose and explanation above is easy to understand. Thanks in advance for any help and suggestions.
Update explanation :
It is a little bit complicated to explain what is my purposes. But I will try to explain as good as I can.
I am trying to make scripts encoder. Encode functions is in encoder scripts side, and decode function is in encoded scripts side. So I must write concatenate decode function before encoded string.
To clearly my explanation, encoder scripts will load undecode source code.
file = io.open(path, "r")
local data = file:read("*l")
The problem is, table cant concatenate with string.
local data = encode(str)--the result is byte array
local data = "decode("..data..")"
file:write(data)
file:close()
local data = string.dump(load(data),true,true)
My first purpose is to hide some important string, because string.dump result is not hide all string.
My second purpose is, to make an obsfucated code using byteArray.
Any solution or suggestion?
SOLVED
function strToBytes(str)
local byteArray= { str:byte(1, -1) }
for i = 1, #byteArray do
byteArray[i] = byteArray[i] + 100
encoded = '{' ..table.concat(byteArray, ',') .. '}'
end
return "load(string.dump(load(bytesToStr("..encoded.."))))()\n"
end
Thank you so much... 👍
Your code was very close to what you were looking for.
--encode
function strToBytes(str)
local byteArray= { str:byte(1, -1) }
for i = 1, #byteArray do
byteArray[i] = byteArray[i] + 100
end
return '{' .. table.concat(byteArray, ',') .. '}'
end
For the encode I removed the table.concat and now just return the byteArray
--decode
function bytesToStr(byteArray)
local output = "" --initialize output variable
for _,b in ipairs(byteArray) do --use ipairs to preserve order
output = output .. string.char(b - 100) --convert each byte to a char and add to output
end
return output
end
For the decode I use a for loop with ipairs to iterate over each byte and concatenate the values into an output variable.
-- test
str = "hello world!"
strbyte = strToBytes(str)
bytestr = 'return bytesToStr(' .. strbyte .. ')'
strBack = string.dump(load(bytestr),true,true)
print(strbyte)
print(bytestr)
print(load(strBack)())
Test output:
{204,201,208,208,211,132,219,211,214,208,200,133}
return bytesToStr({204,201,208,208,211,132,219,211,214,208,200,133})
hello world!

Get numerical values txt file using delimiters

I have a txt file with the following text:
5;2;8;3;
I need get the numeric values, using ; as delimiter, and put them into an array. How could this be achieved?
The easiest way is to just use string.gmatch to match the numbers:
local example = "5;2;8;3;"
for i in string.gmatch(example, "%d+") do
print(i)
end
Output:
5
2
8
3
A "harder" way with a specific Split function:
function split(str, delimiter)
local result = {}
local regex = string.format("([^%s]+)%s", delimiter, delimiter)
for entry in str:gmatch(regex) do
table.insert(result, entry)
end
return result
end
local split_ex = split(example, ";")
print(unpack(split_ex))
Output:
5 2 8 3
Have a look at a sample program here.

Resources