problems with Lua match to find a pattern - lua

I'm struggling with this problem:
Given 2 strings:
s1 = '/foo/:bar/oof/:rab'
s2 = '/foo/lua/oof/rocks'
I would like to produce the following information:
If they match (these two above should match, s2 follows a pattern described in s1).
A table holding the values of s2 in with the corresponding name in s1. In this case we would have: { bar = "lua", rab = "rocks" }
I think this algorithm solves it, but I can't figure how to implement it (probably with gmatch):
store the placeholders : indexes as KEYS of a table, and the respective VALUES being the name of these placeholders.
Example with s1:
local aux1 = { "6" = "bar", "15" = "rab" }
With the keys of aux1 fetched as indexes, extract the values of s2
into another table:
local aux2 = {"6" = "lua", "15" = "rocks"}
Finally merge them two into one table (this one is easy :P)
{ bar = "lua", rab = "rocks" }

Something like this maybe:
function comp(a,b)
local t = {}
local i, len_a = 0
for w in (a..'/'):gmatch('(.-)/') do
i = i + 1
if w:sub(1,1) == ':' then
t[ -i ] = w:sub(2)
else
t[ i ] = w
end
end
len_a = i
i = 0
local ans = {}
for w in (b..'/'):gmatch('(.-)/') do
i = i + 1
if t[ i ] and t[ i ] ~= w then
return {}
elseif t[ -i ] then
ans[ t[ -i ] ] = w
end
end
if len_a ~= i then return {} end
return ans
end
s1 = '/foo/:bar/oof/:rab'
s2 = '/foo/lua/oof/rocks'
for k,v in pairs(comp(s1,s2)) do print(k,v) end

Another solution could be:
s1 = '/foo/:bar/oof/:rab'
s2 = '/foo/lua/oof/rocks'
pattern = "/([^/]+)"
function getStrngTable(_strng,_pattern)
local t = {}
for val in string.gmatch(_strng,_pattern) do
table.insert(t,val)
end
return t
end
local r = {}
t1 = getStrngTable(s1,pattern)
t2 = getStrngTable(s2,pattern)
for k = 1,#t1 do
if (t1[k] == t2[k]) then
r[t1[k + 1]:match(":(.+)")] = t2[k + 1]
end
end
The Table r will have the required result
The solution below, which is some what cleaner, will also give the same result:
s1 = '/foo/:bar/oof/:rab'
s2 = '/foo/lua/oof/rocks'
pattern = "/:?([^/]+)"
function getStrng(_strng,_pattern)
local t = {}
for val in string.gmatch(_strng,_pattern) do
table.insert(t,val)
end
return t
end
local r = {}
t1 = getStrng(s1,pattern)
t2 = getStrng(s2,pattern)
for k = 1,#t1 do
if (t1[k] == t2[k]) then
r[t1[k + 1]] = t2[k + 1]
end
end

Related

how do you make a string dictionary function in lua?

Is there a way if a string is close to a string in a table it will replace it with the one in the table?
Like a spellcheck function, that searches through a table and if the input is close to one in the table it will fix it , so the one in the table and the string is the same?
You can use this code :) Reference code is from here : https://github.com/badarsh2/Algorithm-Implementations/blob/master/Levenshtein_distance/Lua/Yonaba/levenshtein.lua
local function min(a, b, c)
return math.min(math.min(a, b), c)
end
local function matrix(row,col)
local m = {}
for i = 1,row do m[i] = {}
for j = 1,col do m[i][j] = 0 end
end
return m
end
local function lev(strA,strB)
local M = matrix(#strA+1,#strB+1)
local i, j, cost
local row, col = #M, #M[1]
for i = 1, row do M[i][1] = i - 1 end
for j = 1, col do M[1][j] = j - 1 end
for i = 2, row do
for j = 2, col do
if (strA:sub(i - 1, i - 1) == strB:sub(j - 1, j - 1)) then cost = 0
else cost = 1
end
M[i][j] = min(M[i-1][j] + 1,M[i][j - 1] + 1,M[i - 1][j - 1] + cost)
end
end
return M[row][col]
end
local refTable = {"hell", "screen"}
local function getClosestWord(pInput, pTable, threesold)
cDist = -1
cWord = ""
for key, val in pairs(pTable) do
local levRes = lev(pInput, val)
if levRes < cDist or cDist == -1 then
cDist = levRes
cWord = val
end
end
print(cDist)
if cDist <= threesold then
return cWord
else
return pInput
end
end
a = getClosestWord("hello", refTable, 3)
b = getClosestWord("screw", refTable, 3)
print(a, b)
Third parameter is threesold, if min distance is higher than threesold, word is not replaced.

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 ;)

Lua : merge keys and output keys should not have repeative values in table

I am having a table
test = {a= {1,2}, b= {1},c= {2,3}}
I want output like.
test_out = {ab={1,2}, bc = {1,2,3}, ac={1,2,3}}
Apparently what you want are set operations. Here is a way you can do it without any library:
local test = {a = {1, 2}, b = {1}, c = {2, 3}}
local keys = {}
for k,_ in pairs(test) do keys[#keys+1] = k end
table.sort(keys)
local result = {}
local t1, t2, r, found
for i=1,#keys-1 do
for j=i+1,#keys do
t1 = test[keys[i]]
t2 = test[keys[j]]
r, found = {}, {}
for k=1,#t1 do
found[t1[k]] = true
r[k] = t1[k]
end
for k=1,#t2 do
if not found[t2[k]] then
r[#r+1] = t2[k]
end
end
result[keys[i] .. keys[j]] = r
end
end
The result is in the result table. If you can use a set library like pl.Set you can do it with less code:
local test = {a = {1, 2}, b = {1}, c = {2, 3}}
local keys = {}
for k,_ in pairs(test) do keys[#keys+1] = k end
table.sort(keys)
local result = {}
local Set = require "pl.Set"
for i=1,#keys-1 do
for j=i+1,#keys do
result[keys[i] .. keys[j]] = Set.values(
Set(test[keys[i]]) + Set(test[keys[j]])
)
end
end

Lua table.concat

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

How do you reference a table with a key value that is numeric in Lua?

The output for the below script is:
AD[1] = [variable not found]
AD['2'] = bar
How can I modify the function getfield to return a value for v for both cases?
function getfield (f)
local v = _G
for w in string.gfind(f, "[%w_]+") do
v = v[w]
end
return v
end
AD = {[1] = 'foo', ['2'] = 'bar'}
data = {"AD[1]","AD['2']"}
for i,line in ipairs(data) do
s = getfield(line)
if s then
print(line .. " = " .. s)
else
print(line .. " = [variable not found]")
end
end
UPDATE:
I'm 90% sure, this is going to work for me:
function getfield (f)
local v = _G
for w in string.gfind(f, "['%w_]+") do
if (string.find(w,"['%a_]")==nil) then
w = loadstring('return '..w)()
else
w = string.gsub(w, "'", "")
end
v=v[w]
end
return v
end
This happens to work
function getfield (f)
local v = _G
for w in string.gfind(f, "['%w_]+") do
local x = loadstring('return '..w)()
print(w,x)
v = v[x] or v[w]
end
return v
end
AD = {[1] = 'foo', ['2'] = 'bar'}
data = {"AD[1]","AD['2']"}
for i,line in ipairs(data) do
s = getfield(line)
if s then
print(line .. " = " .. s)
else
print(line .. " = [variable not found]")
end
end
but it's pretty fragile.
Note that I added ' to the pattern.
The difficulty is that sometimes w is a string representing a name (key), and sometimes it's a string representing a number. In the second case it needs to be converted from string to number. But you need the context or some syntax to decide.
Here's the kind of fragility I mean:
> data = {"math[pi]","AD['2']"}
>
> for i,line in ipairs(data) do
>> s = getfield(line)
>> if s then
>> print(line .. " = " .. s)
>> else
>> print(line .. " = [variable not found]")
>> end
>> end
math table: 0x10ee05100
pi nil
math[pi] = 3.1415926535898
AD table: 0x10ee19ee0
'2' 2
AD['2'] = bar
> pi = 3
> math[3] = 42
> data = {"math[pi]","AD['2']"}>
> for i,line in ipairs(data) do
>> s = getfield(line)
>> if s then
>> print(line .. " = " .. s)
>> else
>> print(line .. " = [variable not found]")
>> end
>> end
math table: 0x10ee05100
pi 3
math[pi] = 42
AD table: 0x10ee19ee0
'2' 2
AD['2'] = bar
math[pi] is unchanged, but getfield interprets pi in the global context and gets 3 so the wrong field of math is returned.
You'll get the strings '1' and "'2'". You have to evaluate it to turn it into whatever object it is:
v = v[loadstring('return ' .. w)()]
Don't do this if the string came from an untrusted source though (like a user input or something) because they could execute arbitrary code.

Resources