Copying a tables values in Lua is changing the originals value - arrays

I want to copy a simple table however whenever I change the copied table it changes the original table too and it's very bizarre
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
board = {}
print("board: "..tablelength(board))
newBoard = board
newBoard[tablelength(newBoard) + 1] = 1
print("board: "..tablelength(board))
newBoard[tablelength(newBoard) + 1] = 1
print("board: "..tablelength(board))
Output:
board: 0
board: 1
board: 2

newBoard = board only created a new reference newBoard, second after board, to the same table, which was creared by {}.
You need to deep copy a table, if you want a new one.
Something like this:
local function deep_copy( tbl )
local copy = {}
for key, value in pairs( tbl ) do
if type( value ) ~= 'table' then
copy[key] = value
else
copy[key] = deep_copy( value )
end
end
return copy
end
This is a naïve implementation that does not copy metatables and keys that are tables (it's possible in Lua), and tables that include references to themselves, but it's likely to be enough for your purposes.
Further reading: https://www.lua.org/pil/2.5.html, starting from paragraph two.
P.S. A less naïve implementation, based on http://lua-users.org/wiki/CopyTable:
local function deep_copy( original, copies )
if type( original ) ~= 'table' then return original end
-- original is a table.
copies = copies or {} -- this is a cache of already copied tables.
-- This table has been copied previously.
if copies[original] then return copies[original] end
-- We need to deep copy the table not deep copied previously.
local copy = {}
copies[original] = copy -- store a reference to copied table in the cache.
for key, value in pairs( original ) do
local dc_key, dc_value = deep_copy( key, copies ), deep_copy( value, copies )
copy[dc_key] = dc_value
end
setmetatable(copy, deep_copy( getmetatable( original ), copies) )
return copy
end

Related

Finding the Value of an Object Element within an Array

I have a table that im inserting into , The table holds Plate Values
How would i go about iterating over the table to return the following as true
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end
My current table insert
for k, v in pairs(returnVehicleData) do
platesAvailable = v.plate
newTable = {['plate'] = platesAvailable}
table.insert(vehiclePlateTable, newTable)
end
The data thats being returned
[{"plate":"47QVS009"},{"plate":"86KIE632"}]
I want to check the subsequent value after the : on both objects.
I ended up doing this and getting it to work
newValueTable = {}
for Key = 1, #tabledPlate, 1 do
table.insert(newValueTable, tabledPlate[Key].plate)
end
Because the elements are part of an even bigger table of data , they dont actually return as a string. But thank you for the suggestion Coordinate Newton

Csv file to a Lua table and access the lines as new table or function()

Currently my code have simple tables containing the data needed for each object like this:
infantry = {class = "army", type = "human", power = 2}
cavalry = {class = "panzer", type = "motorized", power = 12}
battleship = {class = "navy", type = "motorized", power = 256}
I use the tables names as identifiers in various functions to have their values processed one by one as a function that is simply called to have access to the values.
Now I want to have this data stored in a spreadsheet (csv file) instead that looks something like this:
Name class type power
Infantry army human 2
Cavalry panzer motorized 12
Battleship navy motorized 256
The spreadsheet will not have more than 50 lines and I want to be able to increase columns in the future.
Tried a couple approaches from similar situation I found here but due to lacking skills I failed to access any values from the nested table. I think this is because I don't fully understand how the tables structure are after reading each line from the csv file to the table and therefore fail to print any values at all.
If there is a way to get the name,class,type,power from the table and use that line just as my old simple tables, I would appreciate having a educational example presented. Another approach could be to declare new tables from the csv that behaves exactly like my old simple tables, line by line from the csv file. I don't know if this is doable.
Using Lua 5.1
You can read the csv file in as a string . i will use a multi line string here to represent the csv.
gmatch with pattern [^\n]+ will return each row of the csv.
gmatch with pattern [^,]+ will return the value of each column from our given row.
if more rows or columns are added or if the columns are moved around we will still reliably convert then information as long as the first row has the header information.
The only column that can not move is the first one the Name column if that is moved it will change the key used to store the row in to the table.
Using gmatch and 2 patterns, [^,]+ and [^\n]+, you can separate the string into each row and column of the csv. Comments in the following code:
local csv = [[
Name,class,type,power
Infantry,army,human,2
Cavalry,panzer,motorized,12
Battleship,navy,motorized,256
]]
local items = {} -- Store our values here
local headers = {} --
local first = true
for line in csv:gmatch("[^\n]+") do
if first then -- this is to handle the first line and capture our headers.
local count = 1
for header in line:gmatch("[^,]+") do
headers[count] = header
count = count + 1
end
first = false -- set first to false to switch off the header block
else
local name
local i = 2 -- We start at 2 because we wont be increment for the header
for field in line:gmatch("[^,]+") do
name = name or field -- check if we know the name of our row
if items[name] then -- if the name is already in the items table then this is a field
items[name][headers[i]] = field -- assign our value at the header in the table with the given name.
i = i + 1
else -- if the name is not in the table we create a new index for it
items[name] = {}
end
end
end
end
Here is how you can load a csv using the I/O library:
-- Example of how to load the csv.
path = "some\\path\\to\\file.csv"
local f = assert(io.open(path))
local csv = f:read("*all")
f:close()
Alternative you can use io.lines(path) which would take the place of csv:gmatch("[^\n]+") in the for loop sections as well.
Here is an example of using the resulting table:
-- print table out
print("items = {")
for name, item in pairs(items) do
print(" " .. name .. " = { ")
for field, value in pairs(item) do
print(" " .. field .. " = ".. value .. ",")
end
print(" },")
end
print("}")
The output:
items = {
Infantry = {
type = human,
class = army,
power = 2,
},
Battleship = {
type = motorized,
class = navy,
power = 256,
},
Cavalry = {
type = motorized,
class = panzer,
power = 12,
},
}

How to create an array of pointers to objects in Matlab?

I'm writing a Matlab script where I have a bunch of objects of a same self defined class, say A, B and C. Then I have a function that work on any of the 2 objects, like func(A,B).
Now I want to pick an object, say A, and then func(A,x) through all the other objects. So basically achieve something like:
func(A,B)
func(A,C)
A.update()
func(B,A)
func(B,C)
B.update()
...
So I need to create an array of all the objects I can loop through, while excluding itself of course. I tried to do it with cell array, so I have:
AllObjs = {A,B,C}
for i=1:length(AllObjs)
if ~isequal(A, AllObjs{i})
func(A, AllObjs{i})
end
end
A.update()
However, when A is updated, the A in AllObjs doesn't get updates. So for the next loop I have to create a new array of all the objects. It's doable in this simple example but not manageable when the objects get updated elsewhere. So I would like to have an array of pointers to all the objects. My Google search tells me there's no pointer in Matlab, but is there a way to achieve what I want to do here?
I suspect (its difficult without seeing your code) your classes A, B & C do not inherit from from handle.
Take the examples below:
classdef noHandle
properties
name = '';
end
methods
function obj = noHandle ( name )
obj.name = name;
end
end
end
A = noHandle ( 'A' );
B = noHandle ( 'B' );
C = noHandle ( 'C' );
allObjs = { A B C }
allObjs{1}.name % check its name is "A"
% change the name of A
A.name = 'AAA'
allObjs{1}.name % see that allObjs{1} is still A.
However if you do:
classdef fromHandle < handle
properties
name = '';
end
methods
function obj = fromHandle ( name )
obj.name = name;
end
end
end
Then do:
A = fromHandle ( 'A' );
B = fromHandle ( 'B' );
C = fromHandle ( 'C' );
allObjs = { A B C }
allObjs{1}.name % check its name is "A"
% change the name of A
A.name = 'AAA'
allObjs{1}.name % see that allObjs{1} is updated to AAA.

How to index a table automatically and loop it in ipairs while keeping all data?

Table:
localization_strings = {
string_1 = "Text Here",
string_2 = "Some More Text Here",
string_3 = "More Text"
}
This is obviously not the whole table, but just a small sample. The real table is over 500+ lines. The reason I don't just redo the table is because other functions reference it and I don't have access to those files to fix them, so I have to find a work around. Also, because it would quite tedious work and can cause problems with other codes.
I have made 2 attempts at solving this problem, but I can only get one of the values I want (incorrect terminology, I think) and I need both as 1 is display text and 1 is data for a function call.
Attempts:
-- Attempt #1
-- Gives me the string_#'s but not the "Text"...which I need, as I want to display the text via another function
LocalizationUnorderedOpts = {}
LocalizationOpts = {}
for n,unordered_names in pairs(localization_strings) do
if (unordered_names) then
table.insert( LocalizationUnorderedOpts, n)
end
end
io.write(tostring(LocalizationUnorderedOpts) .. "\n")
table.sort(LocalizationUnorderedOpts)
for i,n in ipairs(LocalizationUnorderedOpts) do
if (n) then
io.write(tostring(i))
table.insert( LocalizationOpts, { text = tostring(LocalizationUnorderedOpts[i]), callback = function_pointer_does_not_matter, data = i } )
end
end
-- Attempt #2
-- Gives me the "Text" but not the string_#'s...which I need to as data to the callback to another function (via function pointer)
LocalizationUnorderedOpts = {}
LocalizationOpts = {}
for n,unordered_names in pairs(localization_strings) do
if (unordered_names) then
table.insert( LocalizationUnorderedOpts, localization_strings[n])
end
end
io.write(tostring(LocalizationUnorderedOpts) .. "\n")
table.sort(LocalizationUnorderedOpts)
for i,n in ipairs(LocalizationUnorderedOpts) do
if (n) then
io.write(tostring(i))
table.insert( LocalizationOpts, { text = tostring(LocalizationUnorderedOpts[i]), callback = function_pointer_does_not_matter, data = i } )
end
end
If I understand it correctly, you need to sort the non-array table. Your first attempt has done most of the work: build another table, which has the values the same as the keys in the original table.
What's left is how to get the original values like "Text Here", for that you need to index the original table:
for k, v in ipairs(LocalizationUnorderedOpts) do
print(v) --original key
print(localization_strings[v]) --original value
end

Lua - check if array exists

I'm trying to find out if a certain array exists via an if statement such as
if array{} == nil then array = {} else print("it exists") end
The above doesn't work it seems and I have no way of checking if it exists, basically I'm creating an AddOn which scans a log for a certain event and if it's true it returns the spellName. I wish to create an array with that spellName, however spellName = {} doesn't work as it seems to just create a new array (rather than updating the existing one).
local _SPD = CreateFrame("Frame");
_SPD:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED");
_SPD:SetScript("OnEvent", function(self, event, ...)
local timestamp, type, sourceName = select(1, ...), select(2, ...), select(5, ...)
if (event == "COMBAT_LOG_EVENT_UNFILTERED") then
if select(2,...) == "SPELL_AURA_APPLIED" then
if select(5,...) == UnitName("player") then
local spellID, spellName = select(12, ...), select(13, ...)
spellName = {
sourceName = {
}
}
table.insert(spellName["sourceName"], {id = spellID, stamp = timestamp })
for k,v in pairs ( spellName["sourceName"] ) do
print (k.. ": " ..v["id"].. " at " ..v["stamp"])
end
end
end
end
end);
Basically it's just re-creating the table every time a certain aura is applied on me (which is expected behavior)
I've banged my head but I have no idea how to check if spellName (and sourceName) exists and if so do not create them again since in this case the variable already exists because it returns the value to me so I can't check if they're nil as they won't be, I need to somehow check if a table exists on those values and if not create them.
Thanks in advance.
Your declaration for table checking is wrong. Use it like this:
if type(array) == "table" then
print("it exists")
else
array = {}
end
Try this:
local spellID, spellName = select(12, ...), select(13, ...)
spellName = spellName or {}
spellName.sourceName = spellName.sourceName or {}
table.insert(spellName.sourceName, {id = spellID, stamp = timestamp })

Resources