remove multiple values from indexed table

Caled
Posts: 403
Joined: Thu Apr 09, 2009 4:45 am

remove multiple values from indexed table

Post 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?

User avatar
Vadi
Posts: 5035
Joined: Sat Mar 14, 2009 3:13 pm

Re: remove multiple values from indexed table

Post by Vadi »

I don't think so. You'd just remember all the indexes in another table and table.remove them in another loop.

Iocun
Posts: 174
Joined: Wed Dec 02, 2009 1:45 am

Re: remove multiple values from indexed table

Post 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.

User avatar
Vadi
Posts: 5035
Joined: Sat Mar 14, 2009 3:13 pm

Re: remove multiple values from indexed table

Post 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...

Iocun
Posts: 174
Joined: Wed Dec 02, 2009 1:45 am

Re: remove multiple values from indexed table

Post 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.

User avatar
Vadi
Posts: 5035
Joined: Sat Mar 14, 2009 3:13 pm

Re: remove multiple values from indexed table

Post by Vadi »

Oh, sorry, I was thinking of removing via table[index] = nil. Should have mentioned that, you are right.

Denarii
Posts: 111
Joined: Thu Dec 03, 2009 10:54 pm

Re: remove multiple values from indexed table

Post 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)

Caled
Posts: 403
Joined: Thu Apr 09, 2009 4:45 am

Re: remove multiple values from indexed table

Post 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.

Denarii
Posts: 111
Joined: Thu Dec 03, 2009 10:54 pm

Re: remove multiple values from indexed table

Post 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)

Denarii
Posts: 111
Joined: Thu Dec 03, 2009 10:54 pm

Re: remove multiple values from indexed table

Post 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.

Post Reply