LUA convert complex table to lowercase - lua

Using LUA how would I convert a table such as this to all lowercase?
return {{[ [[House]] ] = [[bob]],[ [[Roof]] ] = [[steve]],[ [[Door-Knob]] ] = [[Richard]],[ [[Rug-Duty]] ] = [[mark]],},

Assuming your input table is of the following format (I've purposefully mixed upper and lower case characters in it):
test_tbl = {["HOUse"]="BOB", ["Roof"]="STEVE"}
you can write a function as follows:
function convert_table(tbl_test)
local output_table = {}
for i, v in pairs(tbl_test) do
output_table [string.lower(i)] = string.lower(v)
end
return output_table
end
and after calling it: local returned_table= convert_table(test_tbl) you can verify that all keys and values are in lowercase:
for i,v in pairs(returned_table) do
print(i,v)
end

Related

Pandoc filter in Lua to insert span into existing string

I am writing a Lua filter for pandoc that adds a glossary function to HTML output of a markdown file. The goal is to add mouseover text to each occurrence of an acronym or key definition in the document.
I would like to be able to include acronyms when they occur in a list (surrounded by punctuation), but not by letters (e.g. so CO isn't highlighted in a word such as cobalt).
My MWE fails on this count because strings in the Pandoc AST include adjacent punctuation (e.g. Str "CO/DBP/SBP," or Str "CO,",Space,Str "SBP,").
-- # MWE
-- Parse glossary file (summarised here for brevity)
local glossary = {CO = "Cardiac Output", DBP = "Diastolic Blood Pressure", SBP = "Systolic Blood Pressure"}
-- Substitute glossary term for span with a mouseover link
function Str(elem)
for key, value in next, glossary do
if elem.text == key then
return pandoc.Span (key, {title = value, class = "glossary"})
end
end
end
I have had a play with string.sub and string.find but wasn`t able to get anything workable, chiefly because I wasn't sure how to go about returning both the new Span and the Str (minus its new Span). Any help would be appreciated!
My test markdown contains:
# Acronyms: SBP, DBP & CO
Spaced acronyms: CO and SBP and DBP.
In a comma-separated list: CO, SBP, DBP; with backslashes; CO/DBP/SBP, and in bullet points:
* CO
* SBP
* DBP
You can just return a table with multiple elements. My idea was to look for the first separator and then replace the glossary entries with spans:
-- Parse glossary file (summarised here for brevity)
local glossary = {CO = "Cardiac Output", DBP = "Diastolic Blood Pressure", SBP = "Systolic Blood Pressure"}
local Set = function(list)
local set = {}
for i,v in ipairs(list) do
set[v] = true
end
return set
end
local findSeparator = function(text)
local separator = Set{",", "/", " "}
for i = 1, #text do
local s = string.sub(text,i,i)
if separator[s] then
return s
end
end
end
local separatedList = function(text)
local found
local t = {}
local separator = findSeparator(text)
if not separator then return end
for abb in string.gmatch(text, "%P+") do
if glossary[abb] then
found = true
t[#t+1] = pandoc.Span(abb, {title = abb, class = "glossary"})
t[#t+1] = pandoc.Str(separator)
end
end
if found then
-- remove last separator if there are more then one elements in the list
-- because otherwise the seperator is part of the element and needs to stay
if #t > 2 then t[#t] = nil end
return t
end
end
local glossarize = {
Str = function(el)
if glossary[el.text] then
return pandoc.Span(el.text, {title = glossary[el.text], class = "glossary"})
else
return separatedList(el.text)
end
end
}
function Pandoc(doc)
local div = pandoc.Div(doc.blocks)
local blocks = pandoc.walk_block(div, glossarize).content
return pandoc.Pandoc(blocks, doc.meta)
end

Lua Table Access with Location Given as a String

I have a table and I'm trying to access a specific location that is passed in as a String. What is the easiest way to use the string to access the correct location?
Example, if the table looks like this:
a.b1 = true
a.b2.c1 = true
a.b2.c2 = false
a.b3 = true
How can I change a.b2.c2 to true given a location 'a.b2.c2' as a string.
If you have just a single level, you can use square-brace indexing:
function setSingle(obj, key, value)
obj[key] = value
end
setSingle(a, "b1", "foo")
print(a.b1) --> foo
If you have multiple, you need to do several iterations of this indexing. You can use a loop to do that:
function setMultiple(obj, keys, value)
for i = 1, #keys - 1 do
obj = obj[keys[i]]
end
-- Merely "obj = value" would affect only this local variable
-- (as above in the loop), rather than modify the table.
-- So the last index has to be done separately from the loop:
obj[keys[#keys]] = value
end
setMultiple(a, {"b2", "c1"}, "foo")
print(a.b2.c1) --> foo
You can use string.gmatch to parse a properly formatted list of keys. [^.]+ will match "words" made of non-period symbols:
function parseDots(str)
local keys = {}
for key in str:gmatch "[^.]+" do
table.insert(keys, key)
end
return keys
end
Putting this all together,
setMultiple(a, parseDots("b2.c2"), "foo")
print(a.b2.c2) --> foo
One issue you may run into is that you cannot create new tables with this function; you will have to create the containing table before you can create any keys in it. For example, beforing ading "b4.c3" you would have to add "b4".
You can use loadstring to build the statement you want to execute as a string.
a = { b2 = {} }
a.b1 = true
a.b2.c1 = true
a.b2.c2 = false
a.b3 = true
str = "a.b2.c2"
loadstring(str .. " = true")()
print(a.b2.c2)

Lua: Merge 2 strings collections in a case-insensitive manner

I want to merge two strings collections in a case-insensitive manner:
string_collection1 = {"hello","buddy","world","ciao"}
string_collection2 = {"Hello","Buddy","holly","Bye", "bYe"}
merged_string_collection = merge_case_insensitive(string_collection1,string_collection2) --> {"hello","buddy","world","holly","bye","ciao"}
Here's an attempt, but it does not work...
function merge_case_insensitive(t1,t2)
t3 = {}
for _,s1 in pairs(t1) do
for _,s2 in pairs(t2) do
if string.lower(s1) == string.lower(s2) then
t3[s1] = s1
end
end
end
t4 = {}
i = 1
for s,_ in pairs(t3) do
t4[i] = string.lower(s)
i = i + 1
end
return t4
end
string_collection1 = {"hello","buddy","world","ciao"}
string_collection2 = {"Hello","Buddy","holly","Bye", "bYe"}
merged_string_collection = merge_case_insensitive(string_collection1,string_collection2)
for k,v in pairs(merged_string_collection) do print(k,v) end
It does not work because you use == to compare both strings which is case-sensitive.
You could do something like string.lower(s1) == string.lower(s2) to fix that.
Edit:
As you can't figure out the rest yourself, here's some code:
local t1 = {"hello","buddy","world","ciao"}
local t2 = {"Hello","Buddy","holly","Bye", "bYe"}
local aux_table = {}
local merged_table = {}
for k,v in pairs(t1) do
aux_table[v:lower()] = true
end
for k,v in pairs(t2) do
aux_table[v:lower()] = true
end
for k,v in pairs(aux_table) do
table.insert(merged_table, k)
end
merged_table now contains the lower case version of every word in both input tables.
Now pour that into a function that takes any number of input tables and you are done.
What we did here: we use the lower case version of every word in those tables and store them in a list. aux_table[string.lower("Hello")] will index the same value as aux_table[string.lower("hello")]. So we end up with one entry for each word, even if a word comes in multiple variations.
Using the keys saves us the hassle of comparing strings and distiguishing between unique words and others.
To get a table with all strings from two other tables appearing once (without regard to case), you need something like this:
function merge_case_insensitive(t1,t2)
local ans = {}
for _,v in pairs(t1) do ans[v:lower()] = true end
for _,v in pairs(t2) do ans[v:lower()] = true end
return ans
end
string_collection1 = {"hello","buddy","world","ciao"}
string_collection2 = {"Hello","Buddy","holly","Bye", "bYe"}
merged_string_collection = merge_case_insensitive(string_collection1,string_collection2)
for k in pairs(merged_string_collection) do print(k) end
Edit: And in case you want an array result (without adding another iteration)
function merge_case_insensitive(t1,t2)
local ans = {}
local
function add(t)
for _,v in pairs(t) do
v = v:lower()
if ans[v] == nil then ans[#ans+1] = v end
ans[v] = true
end
end
add(t1)
add(t2)
return ans
end
string_collection1 = {"hello","buddy","world","ciao"}
string_collection2 = {"Hello","Buddy","holly","Bye", "bYe"}
merged_string_collection = merge_case_insensitive(string_collection1,string_collection2)
for _,v in ipairs(merged_string_collection) do print(v) end
We can do this by simply iterations over both tables, and storing a temporary dictionary for checking what words we have already found, and if not there yet, putting them in our new array:
function Merge(t1, t2)
local found = {} --Temporary dictionary
local new = {} --New array
local low --Value to store low versions of words in later
for i,v in ipairs(t1) do --Begin iterating over table one
low = v:lower()
if not found[low] then --If not found yet
new[#new+1] = low --Put it in the new table
found[low] = true --Add it to found
end
end
for i,v in ipairs(t2) do --Repeat with table 2
low = v:lower()
if not found[low] then
new[#new+1] = low
found[low] = true
end
end
return new --Return the new array
end
This method eliminates the need for a third iteration, like in Piglet's answer, and doesn't keep redefining a function and closure and calling them like in tonypdmtr's answer.

How do we change the way print displays a table

Assuming I have a piece of code such as the following
aTable = {aValue=1}
aTable_mt = {}
print(aTable)
What must I do to make Lua print something like aTable current aValue = 1 as opposed to table: 0x01ab1d2.
So far I've tried setting the __tostring metamethod but that doesn't seem to be invoked by print. Is there some metamethod I've been missing or does the answer have nothing to do with metamethods?
__tostring works:
aTable = {aValue=1}
local mt = {__tostring = function(t)
local result = ''
for k, v in pairs(t) do
result = result .. tostring(k) .. ' ' .. tostring(v) .. ''
end
return result
end}
setmetatable(aTable, mt)
print(aTable)
This prints aValue 1 (with one extra whitespace, remove it in real code). The aTable part is not available, because aTable is a variable that references the table, not the content of the table itself.
I'm not sure how you set the metamethod, but the following code prints "stringified" for me:
local aTable = {a = 1, b = 2}
setmetatable(aTable, {__tostring = function() return "stringified" end})
print(aTable)
If you want lua to generally print all tables human readable, you could
hook up/overwrite the print function:
local orig_print = print
print = function(...)
local args = {...}
for i,arg in ipairs(args) do
if type(arg) == 'table' then
args[i] = serialize(arg)
end
end
orig_print(table.unpack(args))
end
serialize could be serpent or some other lib from here
Note that this must be done before any other module/script is loaded.

How to get xth key of a table in Lua

I have 2 functions in Lua which create a dictionary table and allow to check if a word exists:
local dictTable = {}
local dictTableSize = 0
function buildDictionary()
local path = system.pathForFile("wordlist.txt")
local file = io.open( path, "r")
if file then
for line in file:lines() do
dictTable[line] = true
dictTableSize = dictTableSize + 1
end
io.close(file)
end
end
function checkWord(word)
if dictTable[word] then
return(true)
else
return(false)
end
end
Now I want to be able to generate a couple of random words. But since the words are the keys, how can I pick some, given the dictTableSize.
Thanks
Just add a numerical index for each word to the dictionary while loading it:
function buildDictionary()
local path = system.pathForFile("wordlist.txt")
local file = io.open( path, "r")
if file then
local index = 1
for line in file:lines() do
dictTable[line] = true
dictTable[index] = line
index = index + 1
end
io.close(file)
end
end
Now you can get a random word like this:
function randomWord()
return dictTable[math.random(1,#dictTable)]
end
Side note: nil evaluates to false in Lua conditionals, so you could write checkWord like this:
function checkWord(word)
return dictTable[word]
end
Another side note, you'll get less polution of the global namespace if you wrap the dictionary functionality into an object:
local dictionary = { words = {} }
function dictionary:load()
local path = system.pathForFile('wordlist.txt')
local file = io.open( path, 'r')
if file then
local index = 1
for line in file:lines() do
self.words[line] = true
self.words[index] = line
index = index + 1
end
io.close(file)
end
end
function dictionary:checkWord(word)
return self.words[word]
end
function dictionary:randomWord()
return self.words[math.random(1,#self.words)]
end
Then you can say:
dictionary:load()
dictionary:checkWord('foobar')
dictionary:randomWord()
Probably two ways: you can keep the array with words and just do words[math.random(#words)] when you need to pick a random word (just make sure that the second one is different from the first).
The other way is to use next the number of times you need:
function findNth(t, n)
local val = next(t)
for i = 2, n do val = next(t, val) end
return val
end
This will return b for findNth({a = true, b = true, c = true}, 3) (the order is undefined).
You can avoid repetitive scanning by memoizing the results (at this point you will be better off using the first way).
this is a trade off that you have for using the word table the way you are. i would invert the word table once you load it, so that you can get references to words by index as well if you have to. something like this:
-- mimic your dictionary structure
local t = {
["asdf"] = true, ["wer"] = true, ["iweir"] = true, ["erer"] = true
}
-- function to invert your word table
function invert(tbl)
local t = {}
for k,_ in pairs(tbl) do
table.insert(t, k)
end
return t
end
-- now the code to grab random words
local idx1, idx2 = math.random(dictTableSize), math.random(dictTableSize)
local new_t = invert(t)
local word1, word2 = new_t[idx1], new_t[idx2]
-- word1 and word2 now have random words from your 'dictTable'

Resources