Editing multidimensional table with uncertain dimensions in Lua - lua

I want to be able to access and edit values in a user-generated table, that can have any number of dimensions.
Say, for this nested table,
table = {
'1',
{
'2.1',
'2.2'
},
{
{
'3.1.1',
'3.1.2'
},
'3.2'
},
}
I would have another table that contains a location for the needed data,
loc = {3, 1, 2}
Ideally, what I'd want is to be able to not only access but edit the values in the table, similar to using table[3][1][2] but by utilizing the loc table,
print(table[loc[1]][loc[2]][loc[3]]) --returns 3.1.2
print(table[loc]) --hypothetically something like this that takes each indexed member of the table in order
I also want to be able to edit this table.
table[loc] = {'3.1.2.1', '3.1.2.2'}
I need to be able to edit the global table, so cannot use the methods listed in this reddit thread, and haven't been able to find the right way to use metatables for this yet. I appreciate the help, thanks.

I think you could simply write an additional function for this purpose.
function TreeGetValue (Tree, Location)
local CorrectTree = true
local Index = 1
local Dimensions = #Location
local SubTable = Tree
local Value
-- Find the most deep table according to location
while (CorrectTree and (Index < Dimensions)) do
local IndexedValue = SubTable[Location[Index]]
if (type(IndexedValue) == "table") then
SubTable = IndexedValue
Index = Index + 1
else
CorrectTree = false
end
end
-- Get the last value, regarless of the type
if CorrectTree then
Value = SubTable[Location[Index]]
end
return Value
end
Here, we assume that the tree is well-formatted as the beginning. If we find any problem we set the flag CorrectTree to false in order to stop immediately.
We need to make sure we have a table at every dimension in order index a value from.
> TreeGetValue(table, loc)
3.1.2
Obviously, it's also easy to to write the set function:
function TreeSetValue (Tree, Location, NewValue)
local Index = 1
local Dimensions = #Location
local SubTable = Tree
-- Find the most deep table according to location
while (Index < Dimensions) do
local IndexedValue = SubTable[Location[Index]]
-- Create a new sub-table if necessary
if (IndexedValue == nil) then
IndexedValue = {}
SubTable[Location[Index]] = IndexedValue
end
SubTable = IndexedValue
Index = Index + 1
end
-- Set or replace the previous value
SubTable[Location[Index]] = NewValue
end
And then to test it with your test data:
> TreeGetValue(table, loc)
3.1.2
> TreeSetValue(table, loc, "NEW-VALUE")
> TreeGetValue(table, loc)
NEW-VALUE

Related

Trying to build a table of unique values in 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

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

Lua - Table.hasValue returning nil

I have a table something like this:
table = {milk, butter, cheese} -- without "Quotation marks"
I was searching for a way to check if a given value is in the table or not, and found this:
if table.hasValue(table, milk) == true then ...
but it returns nil, any reason why? (it says .hasValue is invalid) or can I get an alternative to check if value exists in that table? I tried several ways like:
if table.milk == true then ...
if table[milk] == true then ...
All of these returns nil or false.
you can try this
items = {milk=true, butter=true, cheese=true}
if items.milk then
...
end
OR
if items.butter == true then
...
end
A Lua table can act as an array or as an associative array (map).
There is no hasValue, but by using a table as an associative array you can easily implement it efficiently:
local table = {
milk = true,
butter = true,
cheese = true,
}
-- has milk?
if table.milk then
print("Has milk!")
end
if table.rocks then
print("Has rocks!")
end
You have a few options here.
One, is to create a set:
local set = {
foo = true,
bar = true,
baz = true
}
Then you check if either of these are in the table:
if set.bar then
The drawback to this approach is that you won't iterate over it in any specific order (pairs returns items in an arbitrary order).
Another option would be to use a function to check each value in a table. This'll be very slow in large tables, which brings us to back to a modification of the first option: A reverse lookup generator: (This is what I'd recommend doing -- unless your set is static)
local data = {"milk", "butter", "cheese"}
local function reverse(tbl, target)
local target = target or {}
for k, v in pairs(tbl) do
target[v] = k
end
return target
end
local revdata = reverse(data)
print(revdata.cheese, revdata.butter, revdata.milk)
-- Output: 3 2 1
This'll generate a set (with the added bonus of giving you the index where the value was in your original table). You can also put the reverse into the same table as the data was in, but this won't go well with numbers (and it'll be messy if you need to generate the reverse again).
If you write table = {milk=true, butter=true, cheese=true}, then you can use if table.milk == true then ....

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.

Trying to compare all entries of one table in Lua

Basically I have a table of objects, each of these objects has one particular field that is a number. I'm trying to see if any of these numerical entries match up and I can't think of a way to do it. I thought possibly a double for loop, one loop iterating through the table, the other decrementing, but won't this at some point lead to two values being compared twice? I'm worried that it may appear to work on the surface but actually have subtle errors. This is how I pictured the code:
for i = #table, 1, -1 do
for j = 1, #table do
if( table[i].n == table[j].n ) then
table.insert(table2, table[i])
table.insert(table2, table[j])
end
end
end
I want to insert the selected objects, as tables, into another pre made one without any duplicates.
Let the outer loop run over the table, and let the inner loop always start one element ahead of the outer one - this avoids double counting and comparing objects with themselves. Also, if you call the table you want to examine table that will probably hide the table library in which you want to access insert. So let's say you call your input table t:
for i = 1, #t do
for j = i+1, #t do
if( t[i].n == t[j].n ) then
table.insert(table2, t[i])
table.insert(table2, t[j])
end
end
end
Still, if three or more elements have the same value n you will add some of them multiple times. You could use another table to remember which elements you've already inserted:
local done = {}
for i = 1, #t do
for j = i+1, #t do
if( t[i].n == t[j].n ) then
if not done[i] then
table.insert(table2, t[i])
done[i] = true
end
if not done[j] then
table.insert(table2, t[j])
done[j] = true
end
end
end
end
I admit this isn't really elegant, but it's getting late over here, and my brain refuses to think of a neater approach.
EDIT: In fact... using another table, you can reduce this to a single loop. When you encounter a new n you add a new value at n as the key into your helper table - the value will be that t[i] you were just analysing. If you encounter an n that already is in the table, you take that saved element and the current one and add them both to your target list - you also replace the element in the auxiliary table with true or something that's not a table:
local temp = {}
for i = 1, #t do
local n = t[i].n
if not temp[n] then
temp[n] = t[i]
else
if type(temp[n]) == "table" then
table.insert(table2, temp[n])
temp[n] = true
end
table.insert(table2, t[i])
end
end

Resources