subtract table from table in Lua - lua

I am trying to subtract table from table in Lua, so the return table will be the subtraction of t1 from t2.
This seems to be working but is there a more efficient way of doing so ?
function array_sub(t1, t2)
-- Substract Arrays from Array
-- Usage: nretable = array_sub(T1, T2) -- removes T1 from T2
table.sort( t1 )
for i = 1, #t2 do
if (t2[i] ~= nil) then
for j = 1, #t1 do
if (t2[i] == t1 [j]) then
table.remove (t2, i)
end
end
end
end
return t2
end
local remove ={1,2,3}
local full = {}; for i = 1, 10 do full[i] = i end
local test ={}
local test = array_sub(remove, full)
for i = 1, #test do
print (test[i])
end

Yes, there is: Make a lookup table containing all values of table t1, and then go through table t2 starting at the end.
function array_sub(t1, t2)
local t = {}
for i = 1, #t1 do
t[t1[i]] = true;
end
for i = #t2, 1, -1 do
if t[t2[i]] then
table.remove(t2, i);
end
end
end
Traded O(#t1) space for a speedup from O(#t1*#t2) to O(#t1+#t2).

You simply subtract the table using minus sign.
Ex.
local t2 = {'a', 'b', 'c', 'd', 'e'}
local t1 = {'b', 'e', 'a'}
t2 = t2 - t1
-- t2 new value is {'c', 'd', 'd'}

Related

Remove a object from a table listed inside a different table

So i have 2 tables
local table1 = {1,2,3,4,5,6,7,8,9}
local table2 = {2,4,6,8}
I want remove the number in table 2 from table 1, to then use table one with the numbers removed in more code. How would i go about doing this?
When you read the data into the table, set the value of the index of whatever you are reading in as the key in the table. This avoids collisions and allows easy comparisons against another table.
local table1 = {}
local table2 = {}
-- read in your values as keys into the two tables
-- just an example I have no idea how you are populating the tables but hope it helps
table1 = {['input1'] = 0, ['input6'] = 0, ['input3'] = 0}
table2 = {['input1'] = 0, ['input3'] = 0}
newTable = {}
function tableHasKey(table, key)
return table[key] ~= nil
end
for key, _ in pairs(table1) do
if not tableHasKey(table2, key) then
table.insert(newTable, key)
end
end
for _, value in pairs(newTable) do
print(value)
end
The result is 'input6' when I run this. Which means all values not in table2 that are in table1 are now in newTable. If the values are unique between tables it's easier to compare keys than values. The final result is an indexed table where the result is stored in the value of the hash object.
#! /usr/bin/env lua
local table1 = {1,2,3,4,5,6,7,8,9}
local table2 = {2,4,6,8}
for number = 1, #table1 do
for delete = 1, #table2 do
if table1[number] == table2[delete] then
for i = number, #table1 -1 do
table1[i] = table1[i +1]
end -- shuffle every entry in table down
table1 [#table1] = nil -- erase last entry
end -- number == delete
end -- loop through table2
end -- loop through table1
for i = 1, #table1 do print( table1[i] ) end
1
3
5
7
9
The other answers listed here seem correct, but they seem a little too aggressive on memory or time complexity. Here's my take on a filter function optimized for lists of numbers.
-- t1 : table, the original set of values
-- t2 : table, the set of values to remove from t1
-- returns : table, a subset of elements from t1 not found in t2
local function filter(t1, t2)
-- Assumptions :
-- 1) t1 and t2 are arrays, not dictionaries
-- 2) t1 and t2 do not have mixed indices or mixed values
-- 3) t1 and t2 are sorted
assert(type(t1) == "table", "t1 expected to be a table")
assert(type(t2) == "table", "t2 expected to be a table")
assert(type(next(t1)) == "number" or type(next(t1)) == "nil", "t1 expected to be an array")
assert(type(next(t2)) == "number" or type(next(t2)) == "nil", "t2 expected to be an array")
-- Early Outs :
if #t1 == 0 then
return {}
elseif #t2 == 0 then
return t1
end
-- step through each list and compare each index as you go
local filteredT = {}
local i = 1
local j = 1
local sizeT1 = #t1
local sizeT2 = #t2
while (i <= sizeT1) and (j <= sizeT2) do
if t1[i] == t2[j] then
-- found a match, exclude from output
i = i + 1
j = j + 1
elseif t1[i] < t2[j] then
-- no match, add elements from t1
table.insert(filteredT, t1[i])
i = i + 1
else -- t1[i] > t2[j]
-- no match, ignore elements from t2
j = j + 1
end
end
-- we've made it to the end of one of the lists, add the rest of t1
for i = i, sizeT1, 1 do
table.insert(filteredT, t1[i])
end
return filteredT
end
This solution doesn't have unnecessary loop iterations and a number of early outs for optimization.
local a = {1,2,3,4,5,6,7,8,9}
local b = {2,4,6,8}
local result1 = filter(a, b)
print(table.concat(result1, ", ")) -- 1, 3, 5, 7, 9
local c = {"a", "b", "c", "d"}
local d = {"c", "d"}
local result2 = filter(c, d)
print(table.concat(result2, ", ")) -- "a", "b"

Why won't __add work?

So I am trying to learn about metatables in lua, so i decided to follow some tutorials. I was trying out the __add part of metatables. But for some reason i kept on getting an error (attempt to perform arithmetic on field (nil)
aTable = {}
--Assign the values for the normal table
for x = 1, 10 do
aTable[x] = x
end
-- metatable
mt = {__add = function(table1, table2)
sumTable = {}
for i = 0, #table1 do
sumTable[i] = table1[i] + table2[i]
end
return sumTable
end}
setmetatable(aTable, mt)
newTable = {}
newTable = aTable + aTable
for x = 1, #newTable do
print(newTable[x])
end
At this point i am confused.Help would be appreciated
In the __add-function it should be:
for i = 1, #table1 do
since you didn't set table[0] initially, but started at index 1 (which is indeed recommended for lua-pseudoarrays, many operations rely on it)
#Ctx is correct that the problem is that differing indices in the array initialization and adding functions. But the best way to fix it is to modify your __add function to handle 'holes' in the arrays passed, by checking for nil entries in them.
for i = 0, #table1 do
if (table1[i] and table2[i]) then
sumTable[i] = table1[i] + table2[i]
end
end
Another thing that's missing: You don't set the same metatable on the result, which means that while things like aTable+aTable, aTable+aTable+aTable etc. will work, aTable+aTable+(aTable+aTable) will fail.
Corrected and cleaned version:
-- metatable
mt = {
__add = function( table1, table2 )
sumTable = {}
for i = 1, #table1 do
sumTable[i] = table1[i] + table2[i]
end
return setmetatable( sumTable, mt )
end,
}
aTable = setmetatable( {}, mt )
--Assign the values for the normal table
for x = 1, 10 do aTable[x] = x end
newTable = aTable + aTable
for x = 1, #newTable do print( newTable[x] ) end
-- and a test for what would have failed:
yetAnotherTable = newTable + newTable
for x = 1, #yetAnotherTable do print( yetAnotherTable[x] ) end

How to sort two tables simultaneously by using one of tables order?

Example:
table1 = {2,3,1}
table2 = {a,b,c}
to
table1 = {1,2,3}
table2 = {c,a,b}
This function does not modify either table, and returns the second table sorted according to the first. You can pass a comparison for keys in the first table, like in table.sort.
local sort_relative = function(ref, t, cmp)
local n = #ref
assert(#t == n)
local r = {}
for i=1,n do r[i] = i end
if not cmp then cmp = function(a, b) return a < b end end
table.sort(r, function(a, b) return cmp(ref[a], ref[b]) end)
for i=1,n do r[i] = t[r[i]] end
return r
end
For instance:
local table1 = {2, 3, 1}
local table2 = {"a","b","c"}
local sorted = sort_relative(table1, table2)
print(table.unpack(sorted))
results in:
c a b
I would:
Step 1: Merge the two tables into pairs {{2,a},{3,b},{1,c}}
Step 2: Sort the pairs.
Step 3: Unmerge the resulting array.
table1 = {2,3,1}
table2 = {"a","b","c"}
-- Comparison function
function compare(x, y)
return x[1] < y[1]
end
-- Step 1: Merge in pairs
for i,v in ipairs(table1) do
table1[i] = {table1[i], table2[i]}
end
-- Step 2: Sort
table.sort(table1, compare)
-- Step 3: Unmerge pairs
for i, v in ipairs(table1) do
table1[i] = v[1]
table2[i] = v[2]
end
for i = 1,#table1 do
print(table1[i], table2[i])
end
I use key value pairs and the regular sort function to do the job:
table1 = {2,3,1}
table2 = {"a","b","c"}
table3 = {}
for i, v in ipairs(table2) do
table3[table1[i]] = v
end
table.sort(table1)
table2 = {}
for i = 1,#table1 do
table2[i]=table3[table1[i]]
end
table3=nil
for i = 1,#table1 do
print(table1[i], table2[i])
end
Try this code, which uses the standard function table.sort:
table1 = {2,3,1}
table2 = {"a","b","c"}
table3 = {}
for i,v in ipairs(table1) do
table3[table2[i]]=v
end
table.sort(table1, function (a,b)
return table2[a] <= table2[b]
end)
table.sort(table2, function (a,b)
return table3[a] <= table3[b]
end)
print("table1")
for i,v in ipairs(table1) do
print(i,v)
end
print("table2")
for i,v in ipairs(table2) do
print(i,v)
end

How to shift all elements in a table?

I'm trying to think of an easy way to make all elements in a table shift up one. It is for a game I am playing, attempting to switch between all targets in a table!
For example, let's say I'm surrounded by three mooks who want to kill me, so I target all of them and they're added into an array like so:
{
"mook1",
"mook2",
"mook3",
}
What I want the function to do is change all indexes to go up one (or the amount I specify), and the last to go to the beginning, so the end result would be:
{
"mook3",
"mook1",
"mook2",
}
I attempted it on my own with a simple function like this:
local function nextIndex(tbl, amount)
local t = {}
for k,v in ipairs(tbl) do
if k < #tbl then
t[k+amount] = v
else
t[1] = v
end
end
return t
end
It works as long as the amount is set to 1. I'm sure there is a much smarter and more efficient way of doing this. Could anyone take a whack at it please?!
You can use a function like this:
function wrap( t, l )
for i = 1, l do
table.insert( t, 1, table.remove( t, #t ) )
end
end
You can see a test run on codepad. or, if you're uncomfortable with nesting of function calls;
function wrap( t, l )
for i = 1, l do
table.insert( t, 1, t[#t] )
table.remove( t, #t )
end
end
would work the same way.
I worked a bit more and figured out how to do it. This is the code:
local function nextIndex(tbl, amount)
local t = {}
local i
for k,v in ipairs(tbl) do
i = k + amount
if i <= #tbl then
t[i] = v
else
t[i-#tbl] = v
end
end
return t
end
Is there an easier way to do it though?
So, the task is to rotate the last rot items to the front.
I added parameter n to allow overriding of the sequence end as determined by #t.
-- uses t[#t+1]...t[#t+rot%#t] as scratch space
local function rotate_mod(t, rot, n)
n = n or #t
rot = rot % n
if rot == 0 then return t end
for i = n, 1, -1 do
t[i + rot] = t[i]
end
for i = 1, rot do
t[i], t[i + n] = t[i + n]
end
return t
end
Or if you want a new array (just ignore parameter r):
local function rotate_new(t, rot, n, r)
n, r = n or #t, {}
rot = rot % n
for i = 1, rot do
r[i] = t[n - rot + i]
end
for i = rot + 1, n do
r[i] = t[i - rot]
end
return r
end
Here's a true "in-place" version. It does not need to temporarily enlarge the table:
local function reverse(t, i, j)
while i < j do
t[i], t[j] = t[j], t[i]
i, j = i+1, j-1
end
end
local function rotate_inplace(t, d, n)
n = n or #t
d = (d or 1) % n
reverse(t, 1, n)
reverse(t, 1, d)
reverse(t, d+1, n)
end

Lua : merge keys and output keys should not have repeative values in table

I am having a table
test = {a= {1,2}, b= {1},c= {2,3}}
I want output like.
test_out = {ab={1,2}, bc = {1,2,3}, ac={1,2,3}}
Apparently what you want are set operations. Here is a way you can do it without any library:
local test = {a = {1, 2}, b = {1}, c = {2, 3}}
local keys = {}
for k,_ in pairs(test) do keys[#keys+1] = k end
table.sort(keys)
local result = {}
local t1, t2, r, found
for i=1,#keys-1 do
for j=i+1,#keys do
t1 = test[keys[i]]
t2 = test[keys[j]]
r, found = {}, {}
for k=1,#t1 do
found[t1[k]] = true
r[k] = t1[k]
end
for k=1,#t2 do
if not found[t2[k]] then
r[#r+1] = t2[k]
end
end
result[keys[i] .. keys[j]] = r
end
end
The result is in the result table. If you can use a set library like pl.Set you can do it with less code:
local test = {a = {1, 2}, b = {1}, c = {2, 3}}
local keys = {}
for k,_ in pairs(test) do keys[#keys+1] = k end
table.sort(keys)
local result = {}
local Set = require "pl.Set"
for i=1,#keys-1 do
for j=i+1,#keys do
result[keys[i] .. keys[j]] = Set.values(
Set(test[keys[i]]) + Set(test[keys[j]])
)
end
end

Resources