Trying to build a table of unique values in LUA - lua

I am trying to build a table and add to it at the end each time I get a returned value that is not already in the table. So basically what I have so far is not working at all. I'm new to LUA but not to programming in general.
local DB = {}
local DBsize = 0
function test()
local classIndex = select(3, UnitClass("player")) -- This isn't the real function, just a sample
local cifound = False
if classIndex then
if DBsize > 0 then
for y = 1, DBsize do
if DB[y] == classIndex then
cifound = True
end
end
end
if not cifound then
DBsize = DBsize + 1
DB[DBsize] = classIndex
end
end
end
Then later I'm trying to use another function to print the contents of the table:
local x = 0
print(DBsize)
for x = 1, DBsize do
print(DB[x])
end
Any help would be much appreciated

Just store a value in the table using your unique value as a key. That way you don't have to check wether a value already exists. You simply overwrite any existing keys if you have it a second time.
Simple example that stores unique values from 100 random values.
local unique_values = {}
for i = 1, 100 do
local random_value = math.random(10)
unique_values[random_value] = true
end
for k,v in pairs(unique_values) do print(k) end

Related

Lua finding matching value's from 2 different tables and printing it

local Table1 = {"Test"}
local Table2 = {"'Test' = 'True'"}
--my friend showed me this
local Matched = {}
function has_same()
for _,v in pairs(Table1) do
for _,z in pairs(Table2) do
if v == z then table.insert(Matched, v)
end
end
end
end
has_same()
for i,v in pairs(Matched) do
print(v)
end
This is the code I've been trying to use and when I run it just comes up as blank
I am trying to make it so it prints the Test = True If this needs more explanation let me know

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.

Constructing Key/Value Table in Lua

I'm trying to build a script for a MUD I play that will create a table to keep track of average xp for each mob. I'm having trouble with the syntax of checking whether an element in a table exists and if not creating it. I tried something like this but keep getting: attempt to index field '?' (a nil value)
mobz_buried = {
{mob = "troll", quantity = 2}
{mob = "warrior", quantity = 1}
{mob = "wizard", quantity = 1}} -- sample data
number_of_mobz_buried = 4
xp_from_bury = 2000 -- another script generates these values, these are all just examples
xp_per_corpse = xp_from_bury / number_of_mobz_buried
for _, v in ipairs(mobz_buried) do
if type(mobz[v].kc) == "variable" then -- kc for 'kill count', number of times killed
mobz[v].kc = mobz[v].kc + 1 -- if it exists increment kc
else
mobz[v].kc = 1 -- if it doesn't exist create a key value that matches the mobs name and make the kc 1
end
if type(mobz[v].xp) == "variable" then -- xp for average experience points
mobz[v].xp = (((mobz[v].kc - 1) * mobz[v].xp + xp_per_corpse)/mobz[v].kc) -- just my formula to find the average xp over a range of differant buries
else
mobz[v].xp = xp_per_corpse -- if it doesn't exist create the table just like before
end
end
I'm trying to end up with mobz.troll = {kc, xp}, mobz.warrior = {kc, xp}, mobz.wizard = {kc, xp} and the ability to add more key values based off of the names mobz_buried gives me.
Based on extra info from your comments, it sounds like you didn't construct a table for mobz. Try this:
local mobz = {}
for _, v in ipairs(mobz_buried) do
mobz[v.mob] = mobz[v.mob] or {}
mobz[v.mob].kc = (mobz[v.mob].kc or 0) + 1
-- etc...
end

Remove duplicates from LUA Table by timestamp

I was on stack a few days back for help inserting records to prevent duplicates. However the process to enter these is slow and they slip in.
I have a user base of about 10,000 players, and they have duplicate entries.. I've been trying to filter out these duplicates without success. The examples on stack have no panned out for me.
Here is a clip from my table
[18] =
{
["soldAmount"] = 25,
["buyer"] = [[#playername]],
["timestampz"] = 1398004426,
["secsSinceEvent"] = 55051,
["guildName"] = [[TradingGuild]],
["eventType"] = 15,
["seller"] = [[#myname]],
},
[19] =
{
["soldAmount"] = 25,
["buyer"] = [[#playername]],
["timestampz"] = 1398004426,
["secsSinceEvent"] = 55051,
["guildName"] = [[TradingGuild]],
["eventType"] = 15,
["seller"] = [[#myname]],
},
The timestamp's match and they should not have been added.
for k,v in pairs(sellHistory) do mSavedTHVars.Forever_Sales[k] = v
if mSavedTHVars.Forever_Sales.timestampz ~= sellHistory.timestampz then
table.insert(mSavedTHVars.Forever_Sales, sellHistory)
end end
Now, I need to find out how to remove the current duplicates, and here is what I've tried.
function table_unique(tt)
local newtable = {}
for ii,xx in ipairs(tt) do
if table_count(newtable.timestampz, xx) ~= tt.timestampz then
newtable[#newtable+1] = xx
end
end
return newtable
end
I hope this information provided was clean and understandable.
Thanks!
UPDATE
Attempt #20 ;)
for k,v in pairs(mSavedTHVars.Forever_Sales) do
if v == mSavedTHVars.Forever_Sales.timestampz then
table.remove(mSavedTHVars.Forever_Sales,k)
end
end
No luck yet.
UPDATE
This has worked
for k,v in pairs(mSavedTHVars.Forever_Sales) do mSavedTHVars.Forever_Sales[k] = v
if v.timestampz == mSavedTHVars.Forever_Sales.timestampz then
table.remove(mSavedTHVars.Forever_Sales, k)
end
end
IS this a good approach?
Assuming that mSavedTHVars.Forever_Sales[18] and mSavedTHVars.Forever_Sales[19] are the tables you listed in your post, then to remove all duplicates based on same time stamp it is easiest to create a "set" based on timestamp (since the timestamp is your condition for uniqueness). Loop through your mSavedTHVars.Forever_Sales and for each item, add item to new table only if its timestamp not already in set:
function removeDuplicates(tbl)
local timestamps = {}
local newTable = {}
for index, record in ipairs(tbl) do
if timestamps[record.timestampz] == nil then
timestamps[record.timestampz] = 1
table.insert(newTable, record)
end
end
return newTable
end
mSavedTHVars.Forever_Sales = removeDuplicates(mSavedTHVars.Forever_Sales)
Update based on Question Update:
My comment on following proposed solution:
for k,v in pairs(mSavedTHVars.Forever_Sales) do
mSavedTHVars.Forever_Sales[k] = v
if v.timestampz == mSavedTHVars.Forever_Sales.timestampz then
table.remove(mSavedTHVars.Forever_Sales, k)
end
end
The problem is that I don't see how that can work. When you do for k,v in pairs(mSavedTHVars.Forever_Sales) do then v is mSavedTHVars.Forever_Sales[k] so the next line mSavedTHVars.Forever_Sales[k] = v does nothing. Then if v.timestampz == mSavedTHVars.Forever_Sales.timestampz compares the timestamp of v, i.e. of mSavedTHVars.Forever_Sales[k], with value of a timestampz field in mSavedTHVars.Forever_Sales. But latter is a table without such field, so right-hand-side of == will be nil, so the condition will only be true if v.timestampz is nil, which I don't think is ever the case.
The main reason that I used a solution of creating new table instead of removing duplicates from the existing table is that you can edit a table while iterating over it with pairs or ipairs. If you were to use a reverse counter, it would probably be ok (but I have not tested, test to be sure):
function removeDuplicates(tbl)
local timestamps = {}
local numItems = #tbl
for index=numItems, 1, -1, do
local record = tbl[index]
if timestamps[record.timestampz] ~= nil then
table.remove(newTable, index)
end
timestamps[record.timestampz] = 1
end
end
Also I think the intent of the function is not as clear, but maybe this is just personal preference.

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