Page 1 of 2

remove multiple values from indexed table

Posted: Mon Jul 25, 2011 12:05 pm
by Caled
For me to use table.remove(table, n)
I need to know what 'n' is for the value I wish to remove.
This is easy enough:
Code: [show] | [select all] lua
for i,v in ipairs(table) do
    if v == "somevalueIdon'tlike" then table.remove( table, i) end
end
It gets awkward if I want to remove multiple things though. I can't remove more than one at a time, because using table.remove as I iterate over a table, cancels the iteration. I therefore have to repeat the above loop structure once for every entry that I wish to remove.

Is there another way to be doing something like this?

Re: remove multiple values from indexed table

Posted: Mon Jul 25, 2011 12:27 pm
by Vadi
I don't think so. You'd just remember all the indexes in another table and table.remove them in another loop.

Re: remove multiple values from indexed table

Posted: Mon Jul 25, 2011 9:47 pm
by Iocun
Vadi wrote:I don't think so. You'd just remember all the indexes in another table and table.remove them in another loop.
Well, but you'd run into the same problem with table.remove changing the indexes around then.

Personally, I'd do it without ipairs() in the first place:
Code: [show] | [select all] lua
for n=#mytable,1,-1 do
  if mytable[n]=="some value I don't want" then table.remove(mytable, n) end
end
Since I do a regular for loop from the maximum index to 1, it will not abort until n reaches 1, no matter what happens. I also loop backwards instead of forwards in the table, because that way my indices don't get mixed up, since table.remove() will always move items from high indexes to lower ones to fill in the gaps.

Re: remove multiple values from indexed table

Posted: Mon Jul 25, 2011 10:29 pm
by Vadi
Hm, no, you'd be looping in another loop that removes entries from a different table.

I'm not sure if your way will continue iterating properly...

Re: remove multiple values from indexed table

Posted: Mon Jul 25, 2011 11:11 pm
by Iocun
Vadi wrote:Hm, no, you'd be looping in another loop that removes entries from a different table.

I'm not sure if your way will continue iterating properly...
Correct me if I'm wrong, but I thought you meant something like:
Code: [show] | [select all] lua
local bad_indices = {}
for i,v in ipairs(mytable) do
    if v == "somevalueIdon'tlike" then bad_indices[#bad_indices+1]=i end
end
for _,v in ipairs(bad_indices) do
    table.remove(mytable, v)
end
If that's what you meant, wouldn't that still give you the same problem?
If I have the table {"foo", "somevalueIdon'tlike", "bar", "somevalueIdon'tlike"} it would first make the bad_indices table: {2,4}. So far so good.
Then it would go through that table, and first do table.remove(mytable, 2), resulting in the table {"foo", "bar", "somevalueIdon'tlike"}.
Then it would do table.remove(mytable,4), but since mytable no longer has an index 4, it wouldn't do anything, and we'd end up with the table {"foo", "bar", "somevalueIdon'tlike"}.

I'm pretty sure my method would work, since I've used something similar a few times before.

Re: remove multiple values from indexed table

Posted: Tue Jul 26, 2011 12:13 am
by Vadi
Oh, sorry, I was thinking of removing via table[index] = nil. Should have mentioned that, you are right.

Re: remove multiple values from indexed table

Posted: Wed Jul 27, 2011 9:02 pm
by Denarii
If using custom functions to remove fields isn't a problem, this will create a table that will allow you to remove all fields with a specific value easily. Well, fields with simple types. Because of the way tables and functions are stored internally, it won't remove identical functions/tables. (Unless, possibly, one is a reference to the other.)
Code: [show] | [select all] lua
test = setmetatable({
	__keyindex = {},

	removeall = function(self, value)
		for k in pairs(self.__keyindex[value]) do
			if k == "remove" or k == "removeall" or k == "__keyindex" then return false end
			self[k] = nil
			self.__keyindex[value] = nil
		end
	end,

	remove = function(self, key)
		if v == "remove" or v == "removeall" or v == "__keyindex" then return false end
		local v = self[key]
		self[key] = nil
		self.__keyindex[v][key] = nil
	end,
}, {
	__newindex = function(t, key, value)
		if type(t) == "table" then
			local v = rawget(t, key)
			
			rawset(t, key, value)
			
			if value ~= nil then
				if rawget(t.__keyindex, value) then
					t.__keyindex[value][key] = true
				else
					t.__keyindex[value] = { [key] = true }
				end
			end
			
			return
		end
	 end,
})

test[1] = "test"
test[2] = "test"
test[3] = "test"

display(test)

test:remove(2)

display(test)

test:removeall("test")

display(test)

Re: remove multiple values from indexed table

Posted: Fri Jul 29, 2011 12:29 am
by Caled
Hrm, thanks for that function. I don't understand it, but I'll certainly try it out tonight. If it works (and I must assume it will) it will simplify my script tenfold. Its for picking venom combos based on target's afflictions and my chosen strategy. My stategies are not static though, and so I adjust the strategy depending on the circumstances - i.e. inserting and removing afflictions frequently, to move them up/down the list that forms the priority and ultimately the next combo.

So far the biggest bug is when I get the logic mixed up in my if statements and end up with duplicate affs in the table - doing a slike/slike combo is pretty pointless :/ so the 'removeall' feature you wrote will save me a lot of frustration (and lines of code).

--------------------------

I'm wondering if there could be a function that will create a table as you've created it above.

test = create_table_with_cool_metatable_from_Denarii_built_in()
test[1] = "test"
test[2] = "test"
test[3] = "test"

If it could be that simple it would be a pretty nice improvement on Lua in my opinion.

Re: remove multiple values from indexed table

Posted: Fri Jul 29, 2011 12:36 am
by Denarii
Code: [show] | [select all] lua
function IITable()
	return setmetatable({
		__keyindex = {},

		removeall = function(self, value)
			for k in pairs(self.__keyindex[value]) do
				if k == "remove" or k == "removeall" or k == "__keyindex" then return false end
				self[k] = nil
				self.__keyindex[value] = nil
			end
		end,

		remove = function(self, key)
			local v = self[key]
			if v == "remove" or v == "removeall" or v == "__keyindex" then return false end
			self[key] = nil
			self.__keyindex[v][key] = nil
		end,
	}, {
	__newindex = function(t, key, value)
		if type(t) == "table" then
			local v = rawget(t, key)
			
			rawset(t, key, value)
			
			if value ~= nil then
				if rawget(t.__keyindex, value) then
					t.__keyindex[value][key] = true
				else
					t.__keyindex[value] = { [key] = true }
				end
			end
			
			return
		end
	 end,
	})
end

test = IITable()

test[1] = "test"
test[2] = "test"
test[3] = "test"

display(test)

test:remove(2)

display(test)

test:removeall("test")

display(test)

Re: remove multiple values from indexed table

Posted: Fri Jul 29, 2011 1:57 am
by Denarii
Also, if you're not familiar with using metatables, this is a good example of how they can be useful. I recommend looking through it and trying to get a feel for how it works. Basically, It creates a value => keys index, then when you want to remove every instance of a value, all it has to do is check that index for all the corresponding keys, rather than loop through the table.