Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Is there a way to remove a value from a table without knowing its index?

Hi!

So recently, while developing my game, I've found I've used tables more than I have ever before writing standalone scripts and such, as they're generally useful for saving space, keeping a solid index, etc.

However, I've ran into several occasions where I would have to use table.remove, on say, a player that's in a table, but I don't know the index that the player's in (i.e. 1st value, 2nd value, etc.)

I know it's possible using for loops, cycling through the table until you find the player, and get the player's position in the table that way, but I feel like that's terribly inefficient.

So, my question is-

What's the easiest way to remove a value from a table without knowing what position in the table it has?

Thanks for reading; all replies are appreciated!

1 answer

✓ Accepted answer

Lua doesn't have a "remove this value" function, probably because there's a few ambiguities in what that would mean:

  • Remove one (which?) or all of that value?
  • Remove by just setting to nil or by removing from a list? e.g. {1,2,3} becoming {1,3} or {1,nil,3}

The simplest solution is to remove one value and to pick the appropriate one for the second bullet.

The short answer then, is to remove an element whose index we don't know, we just figure out what the index is. We need to know this index.

There are of course faster ways to search (however this usually will not be prohibitive -- especially not on small data like a list of players playing or even total cumulative players using DataStores).


Linear Search

This is O(n), where n is the size of the table. This is usually fast enough, except when it is repeatedly done (but even for n like 1000 or 2000 this is usually acceptably fast since it is so straightforward to implement and understand)

Lua, for unknown reasons to me, does not have a "final element" function, but it's easy to write one:

function tablefind(tab,el)
	for index, value in pairs(tab) do
		if value == el then
			return index
		end
	end
end

To delete val from tab, we can then use

table.remove( tab, tablefind(tab, val) )

This will only work when your tables are purely lists (using only indices [1,2,3,..]) because table.remove will not work on other numbers. (You would instead use tab[ind] = nil)

Binary Search

For sortable data, like numbers or strings, if you are searching in a sorted list, then you can get away with just O(log(n)) comparisons (e.g., on 2000 elements, just 11 comparisons).

These comparisons are more expensive than in a linear search, though -- and it also requires the data be sorted, and O(n * log(n)) operation -- which is slower than a single (or a small fixed number) of linear searches.

Binary search works by looking if it's higher or lower than a given guess. We can implement it like this:

function binary(tab, val, cmp, low, high)
	-- cmp is a function so that
	-- cmp(a,b) indicates a < b.
	if not low then
		return binary(tab, val, cmp, 1, #tab)
	end
	local middle = math.floor(low/2 + high/2)
	local mid = tab[middle]
	if mid == val then
		return middle
	end
	if low >= high then
		return nil
	end
	if cmp(val,middle) then
		return binary(tab,val,cmp, 1, middle - 1)
	end
	return binary(tab,val, cmp, middle + 1, high)
end

Hashtable

Lua's tables are implemented as hashtables. That means we can extremely quickly find an element at an index -- so if we maintain a second list, we can do the opposite.

whereIsIt = {}
tab = {}

function addTo(val)
	local i = #tab + 1
	tab[i] = val
	whereIsIt = val
end

function remove(val)
	tab[whereIsIt[val]] = nil
	whereIsIt[val] = nil
end

Mathematically, hashtables are O(n^2). However, because of an extra memory usage, they are in practice (for anything within reason, in Lua even tens or hundreds of thousands of values) they are essentially O(1), meaning it ignores the number of elements -- the fastest solution. However, this is less performant in terms of memory -- both in its redundancy and in the inherent form of a hashtable.

With the above stated (hashtable) approach, you cannot efficiently maintain the table as a list -- it will have nil gaps (in fact that #tab will not be "accurate" (as in position of last element) however as it is used in addTo will still yield correct results)

2 comments

0

Thanks, that was a really good quality answer! You say that you can't efficiently maintain a table as a list if you need to interact with it, so are there any alternatives you suggest?

0

If you are using a hashtable, simply "dealing with it" is probably the best way to go -- you can still use pairs to loop over all values, it will just be skipping indexes. However, again, I think the linear approach is probably what you should go with -- what is the data that you are going over?

This discussion is preserved as it was. New answers, comments, and votes are closed.