## unintentional change to a table

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
IAmTheRealSpartacus
Prole
Posts: 9
Joined: Mon Nov 07, 2011 8:08 pm

### unintentional change to a table

Hi,

I'm trying to do the following thing.

I have a table, say

Code: Select all

  set = { { name = "gold",    amount = 10   },
{ name = "silver",  amount = 100  } }

Now I wrote a function that can add values to it from a similar set, like such:

Code: Select all

function addSet(setToBeAdded, s)

local setToBeReturned = s

for k1, v1 in ipairs(setToBeAdded) do
for k2, v2 in ipairs(setToBeReturned) do
if v1.name == v2.name then
v2.amount = v2.amount + v1.amount
end
end
end

return setToBeReturned
end
The problem is that, even though I'm adding values to the table 'setToBeReturned', the new values show up in the original table, 'set', even though I explicitly tell the function to copy the values from the original table into the table 'setToBeReturned'. This gives problems in the next program, where I'm telling Lua to stop substracting from the main table if one of the values is zero. It recognizes that but still keeps on substracting. what am I doing wrong? (instructions: run code and press spacebar 11 times to see the amount of gold go negative)

Code: Select all

local set = {}
local success

set = { { name = "gold",    amount = 10   },
{ name = "silver",  amount = 100  } }

success = true

end

function love.draw()

for k, v in ipairs(set) do
love.graphics.print(v.amount, 0, k * 20)
end

if success == true then love.graphics.print("can substract", 100, 0)
else love.graphics.print("cannot substract", 100, 0)
end

end

function love.keypressed(key, unicode)

-- when spacebar is pressed...
if unicode == 32 then

--add increment to set and store result in newSet
increment = { { name = "gold", amount = -1} }

--check if none of the values are negative
success = true

for k, v in ipairs(newSet) do
if v.amount < 0 then
success = false
end
end

--if none of the values are negative, copy newSet into set
if success == true then set = newSet end

end

if key == "q" or key == "escape" then
love.event.push("q")
end

end

local setToBeReturned = s

for k1, v1 in ipairs(setToBeAdded) do
for k2, v2 in ipairs(setToBeReturned) do
if v1.name == v2.name then
v2.amount = v2.amount + v1.amount
end
end
end

return setToBeReturned
end


Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

### Re: unintentional change to a table

IAmTheRealSpartacus wrote:The problem is that, even though I'm adding values to the table 'setToBeReturned', the new values show up in the original table, 'set', even though I explicitly tell the function to copy the values from the original table into the table 'setToBeReturned'.
No, you don't copy anything. Lua never copies anything. a = b means "a points to whatever b points to", not "the value of b is copied and put in a".

This might be what you want:

Code: Select all

function addSet(setToBeAdded, s)

local setToBeReturned = {}

for k, v in ipairs(s) do
setToBeReturned[k] = {v.name, v.amount}
end
for k1, v1 in ipairs(setToBeAdded) do
for k2, v2 in ipairs(setToBeReturned) do
if v1.name == v2.name then
v2.amount = v2.amount + v1.amount
end
end
end

return setToBeReturned
end
This is still not an optimal solution, though.

EDIT:
This should make it O(n) instead of O(n^2):

Code: Select all

function addSet(setToBeAdded, s)
local temp = {}
local setToBeReturned = {}

for k, v in ipairs(s) do
temp[v.name] = v.amount
end
for k, v in ipairs(setToBeAdded) do
temp[v.name] = temp[v.name] + v.amount -- you can add a check to see if it exists in temp if you want
end

for k, v in pairs(temp) do
setToBeReturned[#setToBeReturned+1] = {name = k, amount = v}
end
return setToBeReturned
end
Note it does not preserve order, though. (So the items may be shuffled.)

rhezalouis
Party member
Posts: 100
Joined: Mon Dec 07, 2009 10:27 am
Location: Indonesia
Contact:

### Re: unintentional change to a table

Hi TheRealSpartacus,

First, you might want to change your data structure (or how you define this set in Lua) because you actually could use the item names (i.e. strings) as the key for your table instead of storing it inside the table.
So, instead of using this array:
IAmTheRealSpartacus wrote:

Code: Select all

set = { { name = "gold",    amount = 10   },
{ name = "silver",  amount = 100  } }
your set would be better if defined like this hash table (single value per entry):

Code: Select all

tAmount = {
gold = 10;
silver = 100;
}
tPrice = {
gold = 150;
silver = 30;
}
or if you want these several values (amount and price) to be put together in the same entry:

Code: Select all

tInventory = {
gold = {amount = 10; price = 150};
silver = {amount = 100; price = 30}
}
So when you update an entry in this table, your function could instantly pick the relevant entry; it doesn't have to iterate through all of the entries just to find the entry you want to manipulate.

Second, regardless of this data type suggestion, the issue you had is happening because when you do:

Code: Select all

set = {}
you are actually doing two things:
1. create an anonymous table
2. store the reference to that table in the variable set
so when you create a local variable like this:
IAmTheRealSpartacus wrote:

Code: Select all

local setToBeReturned = s
you only pass the reference number (which is used to access the underlying anonymous table) from variable set to local variable setToBeReturned. Lua won't create you a new table for that local variable (remember that a table is only created using this ' = {}' operation). Which means when you make any changes to 'this local variable', you actually change the table that is referenced by it. To show this you could do:

Code: Select all

a = {1}           --create a table at an address; store the address to a variable
local b = a       --copy the reference/address to that table to a local variable
print(a, b)       -->table: 08DFE790	table: 08DFE790 --as you can see, these variables are representing the same object
print(a[1], b[1]) -->1	1
b[1] = 2          --changes made using the reference stored in the local variable 'b' would affect 'a'
print(a[1], b[1]) -->2	2
Try to use the new data structure and find out another way to implement your requirement.

P.S. Hi Robin, let me ftfy.
Robin wrote:Lua never copies anything-being-referenced but its reference.
Aargh, I am wasting my posts! My citizenshiiiip...

IAmTheRealSpartacus
Prole
Posts: 9
Joined: Mon Nov 07, 2011 8:08 pm

### Re: unintentional change to a table

Thanks a lot guys! I'm new to Lua and I figured that at some point I'd get things confused with other languages.
Lua never copies anything. a = b means "a points to whatever b points to", not "the value of b is copied and put in a".
Robin: In hindsight I should have seen this coming

rhezalouis: thanks, I think the following has become my preferred data structure:

Code: Select all

tInventory = {
gold = {amount = 10; price = 150};
silver = {amount = 100; price = 30}
}
As I won't need a price field, would it suffice to say:

Code: Select all

tInventory = { gold = 10; silver = 100 }
?

Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

### Re: unintentional change to a table

IAmTheRealSpartacus wrote:As I won't need a price field, would it suffice to say:

Code: Select all

tInventory = { gold = 10; silver = 100 }
?
Sure, that is in fact the structure of the "temp" table in my second suggestion.

IAmTheRealSpartacus
Prole
Posts: 9
Joined: Mon Nov 07, 2011 8:08 pm

### Re: unintentional change to a table

Robin wrote:
IAmTheRealSpartacus wrote:As I won't need a price field, would it suffice to say:

Code: Select all

tInventory = { gold = 10; silver = 100 }
?
Sure, that is in fact the structure of the "temp" table in my second suggestion.
I've tried it out but for some reason the table appears empty... At least according to table.getn. See this code where I try to print the table:

Code: Select all

local t = {}

function love.draw()

t = { gold = 10; silver = 100 }

love.graphics.setColor(255,255,255,255)

if table.getn(t) == 0 then love.graphics.print("empty", 0, 0) end

i = 0
for k, v in ipairs(t) do
love.graphics.print(k,   0, i * 10)
love.graphics.print(v, 100, i * 10)
i = i + 1
end

end
I end up with a black screen and white letters saying "empty". What's going wrong?

thelinx
The Strongest
Posts: 847
Joined: Fri Sep 26, 2008 3:56 pm
Location: Sweden

### Re: unintentional change to a table

table.getn (which is deprecated, use #myTable) only counts the sequence part of the table. (The part that's indexed by numbers, not keys)

ipairs also only loops through the sequence part. Use pairs(myTable) to loop through all values in the table.

IAmTheRealSpartacus
Prole
Posts: 9
Joined: Mon Nov 07, 2011 8:08 pm

### Re: unintentional change to a table

thelinx wrote:table.getn (which is deprecated, use #myTable) only counts the sequence part of the table. (The part that's indexed by numbers, not keys)

ipairs also only loops through the sequence part. Use pairs(myTable) to loop through all values in the table.
I feel like such a noob Thanks!

### Who is online

Users browsing this forum: CogentInvalid, Google [Bot] and 5 guests