table.insert/remove by value - lua

I got two tables, for example:
table1 = { element1, element2, element3, element4 }
table2 = { element1, element3 }
Table 2 refers to some elements of table1, but I don't know which exactly, nor I know their index.
Now, for an specific element I want to check if table2 does contains it or not and insert/remove it in the case.
First thing that jumped to my mind was:
table.remove/insert(table2, table1.elementX)
But due insert/remove does its lookup by index, this doesn't work.
Sure, I could iterate through the whole table until I find the element and remove it, respectively until Iteration is done without match and insert it.
But is there a more performant method to do this?
I do not want to fill table2 with empty fields for bringing the elements on matching indices.

To insert, it's quite straightforward:
table.insert(table1, table2[index])
Unfortunately, to remove, it's a bit more tricky:
local ids = {} -- table containing ids to remove
for i,v in ipair(table1) do
if v == table2[index] then
table.insert(ids, 1, i) -- "1" preprends the value
end
-- At this point, "ids" contains all the ids to remove in the reverse order
for k,v in pair(ids) do
table.remove(table1, v)
end
What happens here is:
An intermediate table is created, it only contains the ids of the table to remove, descending. For example: { 6, 3, 1} (if the value is present 3 times).
That intermediate table is used to update the main table, as you can't use the ids from a table you are updating in a loop (that's what the comments about "transversal" mean).
Note that those operations must be made from the end of the table, because removing an element will change the ids of the following ones.

First revert table2 with
table2reverse = {}
for k,v in pairs(table2) do table2reverse[v]=k end
Then do this:
for k,v in pairs(table1)do
if table2reverse[v] then
table1[k]=nil
end
Finally compact table1.

for k,v in pairs(table1)do
if v == table2[index] then
table.remove/insert(table1, k)
break
end
Of course this works, but I still hope there's a more performante solution.
Due in case of multiple 1000 entrys in table1 and multiple 100 entrys in table2, this is will lead to high cpu usage, wich I want to avoid. (programming a controller with only 200mhz)

Related

How to remove all elements found in GROUP B to GROUP A in Lua

Let's say I have two groups defined in my Lua script
groupA = {"donkey", "goat", "eagle", "whale", "dolphine", "dog", "mosquito", ...}
groupB = {"goat", "mosquito", "donkey"}
After the remove operation, the value of groupA have no more elements: "goat", "mosquito", and "donkey"
How do I remove all items in groupA that are found in groupB. I know we can loop through the items and compare each one but I prefer any API or simple built in statements that solve this types of problem. The elements could also be any type like record.
There are no built-in operators that calculate set difference in Lua. You can do what you described and to speed up this process you can build a hash of elements from the second table and then iterate over the elements in the first table and check if they are present in the hash (of the elements in the second table).
If you end up using table.remove to remove elements from the first table while iterating, you need to be careful to iterate from the end, otherwise you may end up skipping elements you need to remove.
You can also check if some of the suggestions in this thread about set operators work for you.
local lookup = {}
for i, v in ipairs(groupB) do
lookup[v] = true
end
local answer = {}
for i, v in ipairs(groupA) do
if (not lookup[v]) then
table.insert(answer, v)
end
end
create a lookup table for the unique items in groupB
traverse groupA and lookup each item in the lookup table
add items from groupA not found in the lookup table to answer table
Note: this approach doesn't account for duplicates. For example, if groupB contains "goat" three times, and groupA contains "goat" four times, then answer will contain "goat" zero times.
After some time of researching, I found out that this simple subtraction in Lua works for groups (or table) to remove elements found in a group from another.
Ex.
groupA = groupA - groupB

Potential problems with storing the second table of two dimensional arrays in the index

In the past I found myself using a table as index and value of
a table when the order was irrelevant.
Since every table returns a unique value they are save to use as
index and with that I already got all the information I want to
use later on in the program. Now I did not see any similar lua code
jet and didn't use it in a non test-program. So I'm worrying that I
might get some unforeseen/unexpected problems when using this method.
example:
a = {1,2,3,4,5} --some testing values
b = {2,nil,4,nil,1}
c = {3,nil,nil,nil,2}
d = {4,nil,1,nil,3}
e = {5,1,2,3,4}
tab = {a,b,c,d,e}
t = {}
for i, v in pairs(tab) do
t[v] = 0
end
for iv in pairs(t) do --is almost every time outputting it in a different order
print(iv[1],iv[2],iv[3],iv[4],iv[5]) --could be a list of data where you have to go through all of it anyway
end
io.read()
Now I can store some additional information in t[v] but if I don't have
any is there maybe some lua-type that is smaller?
Edit:
Does this go well with the use of weak-tables?
Note:
Standard 2d table: table[key1] = table
table[key1][key2] <-- contains stuff
this version: table[table] = anything but nil <-- not accessible over table[key1][key2]
key1[key2] <-- contains stuff
It's fine to use a table as a key in another table.
However, note that different tables will be different keys, even of the tables have the same contents.

remove all entries with specified value in table

I want to delete all entries from a table, wich equals a given value.
Now, I got a pretty little problem one might to know, how to handle.
This is the Code:
function(list_to_search_in, compared_value, invert)
for k,v in pairs(list_to_search_in) do
if invert and v ~= compared_value then
table.remove(list_to_search_in, v)
if not invert and v == compared_value then
table.remove(list_to_search_in, v)
end
end
end
The Problem:
Let's say the table is { 1, 2, 3, 2 }. So when I'm iterating through that loop and come to the first match, it's removed from the table. This means the value and the key is deleted.
Now the key of the deleten value is assigned to the next value in line. But due the skript will check the value of the next key, this value (whichs kay has been just altered) will never be checked.
I thought, a simple
k = k - 1
after a remove would do the job, but it doesn't.
v = nil
would do great I think, but only if garbage-collector does not do his job in this very moment the pairs iterates to the next value.
Anyone has an idea? I would prefer an text-based hint to a finished syntax which solves the problem.
Don't use table.remove for this. It squeezes the "hole" out of array-like tables. That's not allowed during an iteration using pairs/next. Just set the value to nil.
If you need to squeeze holes out of the table then you can either create a new table and populate it with only the values you want to keep or do the removals during the first pass and then squeeze out holes in a second pass.
Also the order of item traversal when using pairs is not guaranteed in any way.

Assign table to table in Lua

In Lua, I can add an entry inside table with table.insert(tableName, XYZ). Is there a way I can add already existing table into table? I mean a directly call rather then traversing and add it.
Thanks
The insert in your example will work fine with whatever contents happen to be inside the XYZ variable (number, string, table, function, etc.).
That will not copy the table though it will insert the actual table. If you want to insert a copy of the table then you need to traverse it and insert the contents.
First: In general, you do not need table.insert to put new entries into tables.
A table in Lua is a collection of key-value pairs; entries can be made like this:
local t = {} --the table
local key= "name"
local value = "Charlie"
t[key] = value --make a new entry (replace an existing value at the same key!)
print(t.name) --> "Charlie"
Note that key can have any type (not just integer/string)!
Very often you will need tables for a simple special case of this: A sequence ("list", "array") of values. For Lua, this means you want a table where all the keys are consecutive integers, and contain all non-nil values. The table.insert function is intended for that special case: It allows you to insert a value at a certain position (or to append it at the end of the sequence if no position is specified):
local t = {"a", "b", "d"} --a sequence containing three strings (t[1] = "a", ...)
table.insert(t, "e") --append "e" to the sequence
table.insert(t, 3, "c") --insert "c" at index 3 (moving values at higher indices)
--print the whole sequence
for i=1,#t do
print(t[i])
end
If I understand what you mean correctly, you want to do this:
local t1 = {1, 2, 3}
local t2 = {4, 5, 6}
some_function(t1, t2)
-- t1 is now {1, 2, 3, 4, 5, 6}
There is indeed no way to do this without iterating t2. Here is a way to write some_function:
local some_function = function(t1, t2)
local n = #t1
for i=1,#t2 do t1[n+i] = t2[i] end
end
No, you must copy the second table's key/value pairs into the first table. Copying the existing values from the second table is what's known as a "shallow copy." The first table will reference the same objects as the second table.
This works under limited circumstances:
local n = #t1
for i=1,#t2 do t1[n+i] = t2[i] end
It does attempt to shift the t2 elements to just beyond the existing t1 elements. That could be a vital requirement but wasn't stated in the question.
It has a few of problems, though:
By using #t1 and #t2, it misses keys that aren't positive integers and can miss keys that are integers greater than a skipped integer key (i.e. never assigned or assigned nil).
It accesses the first table with an indexer so could invoke the __newindex metamethod. That probably wouldn't be desirable when only copying is wanted.
It accesses the second table with an indexer so could invoke the __index metamethod. That wouldn't be desirable when only copying is wanted.
You might think that ipairs could be used if only positive integer keys are wanted but it quits on the first nil value found so could miss even more than #t2 does.
Use pairs instead:
for key, value in pairs(t2) do
rawset( t1, key, value )
end
If you do want to avoid replacing existing t1 values when the keys match or otherwise map t2 keys in some way then that has to be defined in the requirements.
Bottom line: pairs is the way to get all the keys. It effectively does a rawget so it avoids invoking __index. rawset is the way to do a copy without invoking __newindex.

Lua: Smartest way to add to table only if not already in table, or remove duplicates

I have a table of strings. I'd like an easy way to remove all of the duplicates of the table.
So if the table is {a, b, c, c, d, e, e} , after this operation it would be {a, b, c, d, e}
Alternatively, and probably preferably, is there a way to add an element to a table, but only if it is not already contained within the table.
<\noobquestion>
What I normally do for this is index the table on the string so for example
tbl[mystring1] = 1
tbl[mystring2] = 1
etc.
When you add a string you simply use the lines above and duplicates will be taken care of. You can then use a for ... pairs do loop to read the data.
If you want to count the number of occurrences
use something like
if tbl[mystring1] == nil then
tbl[mystring1] = 1
else
tbl[mystring1] = tbl[mystring1] + 1
end
As the end of the addition cycle if you need to turn the table around you can simply use something like
newtbl = {}
for s,c in pairs(tbl) do
table.insert(newtbl,s)
end
It sounds like you're trying to implement a Set, a collection of unique elements. This article might help you: http://www.lua.org/pil/13.1.html
The simplest way is using the tables as keys, not as values, in your "container table".
Let's call the container table values. You must currently be doing something similar to this for adding elements to it:
table.insert(values, value)
And you parse values like this:
for i,v in ipairs(values) do
-- v contains the internal values
end
In order to have the tables just once, you can insert them this other way:
values[value] = 1
This will ensure that the inserted values (strings, tables, numbers, whatever) are included just once, because they will be 'overwritten'.
Then you can parse values like this:
for k,_ in pairs(values) do
-- k contains the internal tables
end

Resources