Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
3

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

Asked by 10 years ago

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

Log in to vote
4
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
10 years ago

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)

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? Tortelloni 315 — 10y
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? BlueTaslem 18071 — 10y
Ad

Answer this question