Can I only extract the same value from two tables? [closed] - arrays

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
What is the best way, in terms of code cleanness or efficiency, to find an "intersection" of two Lua numbered tables, i.e. a table that contains only values that are present in both tables?
For example, I have
a={1,2,3,4,5}
b={3,4,5,6,7}
Since 3, 4 and 5 are found in both a and b, I want {3,4,5}.

I suggest a slightly different approach, based on sets. The performance will not necessarily improve, bit the code will be clean, understandable and expandable.
In Lua a set can be easily implemented as a table, where the keys are set items and the values are true, therefore, to check whether an item is present in the set, you just need if set[item] then.
I will give three variants of the code. The first cleanly implements an intersect operator but then has to transform the resulting set back to an indexed Lua table. The second immediately returns the intersections of two sets as an array. The third is a compromise between the first two.
Note that neither contains nested loops over a and b: it's O(a + b) rather than O(ab).
First:
local insert, sort = table.insert, table.sort -- localise for performance.
local a = {1,2,3,4,5}
local b = {3,4,5,6,7}
local function toset (tbl)
local set = {}
for _, value in ipairs (tbl) do
set [value] = true
end
return set
end
local function intersect (set1, set2)
local intersection = {}
for item, _ in pairs (set1) do
if set2 [item] then
intersection [item] = true
end
end
return intersection
end
local function toarray (set)
local array = {}
for item, _ in pairs (set) do
insert (array, item)
end
sort (array) -- since you want {3,4,5} not {4,5,3}
return array
end
-- Fortunately, table.concat does not require previous tostring:
print ('{' .. table.concat (toarray (intersect (toset (a), toset (b))), ', ') .. '}')
Second:
local insert, sort = table.insert, table.sort -- localise for performance.
local a = {1,2,3,4,5}
local b = {3,4,5,6,7}
local function toset (tbl)
local set = {}
for _, value in ipairs (tbl) do
set [value] = true
end
return set
end
local function intersect_and_to_array (set1, set2)
local array = {}
for item, _ in pairs (set1) do
if set2 [item] then
insert (array, item)
end
end
sort (array) -- since you want {3,4,5} not {4,5,3}
return array
end
-- Fortunately, table.concat does not require previous tostring:
print ('{' .. table.concat (intersect_and_to_array (toset (a), toset (b)), ', ') .. '}')
Third:
local insert = table.insert -- localise for performance.
local a = {1,2,3,4,5}
local b = {3,4,5,6,7}
local function toset (tbl)
local set = {}
for _, value in ipairs (tbl) do
set [value] = true
end
return set
end
local function filter_array_through_set (array, set)
local filtered = {}
for _, value in ipairs (array) do
if set [value] then
insert (filtered, value)
end
end
return filtered -- the order of array is preserved.
end
-- Fortunately, table.concat does not require previous tostring:
print ('{' .. table.concat (filter_array_through_set (a, toset (b)), ', ') .. '}')

You need to iterate on one of the table and output the value if you find it in the other table. On, my computer the following code returns {3, 4, 5}, it basically implement the intersection of 2 sets.
a={1,2,3,4,5}
b={3,4,5,6,7}
function Contains (Array, Item)
local Found = false
local Index = 1
while ((not Found) and (Index <= #Array)) do
if Array[Index] == Item then
Found = true
else
Index = Index + 1
end
end
return Found
end
function Intersects (Ta, Tb)
local Result = { }
local Index
for Index = 1, #Tb do
if Contains(Ta, Tb[Index]) then
Result[#Result + 1] = Tb[Index]
end
end
return Result
end
Result = Intersects(a, b)
-- Print the results
local Index, Value
for Index, Value in ipairs(Result) do
print(Value)
end

Related

How to i compare the value in array for similar regardless the order of index

I am new to Lua and love using array but not good at it. The code that I have is a simple array comparison between 2 array. but what I wanted to do was to compare any value inside the array between number and number1. as showed in "my current code" below, there should be 2 digit in different index from both side and I want to print "2 number matches!" and etc.
I also want to compare from an array1 with {1,2,4} and array2 with {2,1,4} and print("Match!"). A simple "==" did not work as it compare each value from the same index of both array1 and array2. I tried googling all this problem of mine but didn't get what I wanted... Sorry, just learned array not long ago
my current code
local number = {5,6,7,8,9,4}
local number1 = {3,2,4,6,1}
if number == number1 then
print("Matched!")
else
print("No Match")
end
Expectation (example of what i wanted to have but in code)
local array1 = {5,6,7,8,9,4}
local array2 = {3,2,4,6,1}
local array3 = {6,5,8,4,9,7}
if array1 == array2 then
print("Matched!")
elseif array2 has x amount of similarities to array1
print(x .."Matched!") -- this will print 2 or more matches
else
print("No Match!")
end
if array3 have similar value regardless of the index of array1 then
print("Matched!")
else
print("No Match!")
end
You can convert your arrays into sets, implemented as item => true tables, and use set arithmetics:
local set_mt = {}
-- Set difference (set1 - set2):
function set_mt.__sub (set1, set2)
local diff = {}
for value, _ in pairs (set1) do
if not set2 [value] then
diff [value] = true
end
end
return setmetatable (diff, set_mt)
end
-- Set intersection (set1 ^ set2):
function set_mt.__pow (set1, set2)
local intersect = {}
for value, _ in pairs (set1) do
if set2 [value] then
intersect [value] = true
end
end
return setmetatable (intersect, set_mt)
--[[ Also possible:
return set1 - (set1 - set2)
--]]
end
-- Set cardinality (#set):
function set_mt.__len (set)
local len = 0
for value, _ in pairs (set) do
len = len + 1
end
return len
end
-- Set equality (set1 == set2):
function set_mt.__eq (set1, set2)
return #(set1 - set2) == 0 and #(set2 - set1) == 0
--[[ Also possible:
return #set1 == #set2 and #(set1 ^ set2) == #set1
--]]
end
--[[ Also possible, though not required by the task:
union, as __add,
cartesian product as __mul,
is subset of as __le,
serialisation as __tostring,
absolute complement as __umn, if there is a universum, though it will make everything more complex.
--]]
local function toset (array)
local set = {}
for _, value in ipairs (array) do
set [value] = true
end
return setmetatable (set, set_mt)
end
-- Test:
local array1 = {5,6,7,8,9,4}
local array2 = {3,2,4,6,1}
local array3 = {6,5,8,4,9,7}
local set1, set2, set3 = toset (array1), toset (array2), toset (array3)
local x = #(set2 ^ set1)
if set1 == set2 then
print 'array1 and array2 matched!'
elseif x >= 2 then
print ('array2 has ' .. tostring (x) .. ' matches in array1') -- this will print 2 or more matches
else
print 'No Match!'
end
if set3 == set1 then
print 'array3 and array1 matched!'
else
print 'No Match!'
end

Lua doesn't go over 'nil' in table

I have such an anonymous Lua function:
an_func = function(x) return x == nil end
I need to test it on a table from 4 values: {'None', nil, 1, '1'}.
So, I wrote the following code:
for num, element in pairs({'None', nil, 1, '1'}) do
print(num .. ': ' .. tostring(an_func(element)))
end
When I ran it, I got only three lines:
1: false
3: false
4: false
The line with the true value (corresponding to nil table element) didn't print.
Can you explain, why did Lua print all results, but true? How can I fix the snipped make it output all 4 lines?
P.S. I'm newbee in Lua.
When using pairs(), it goes over all elements of a table. nil means nothing, so it won't go over it. Why should it tell you there's nothing in the table? If you have a totally new table, it'll return nil if you index it with anything. print(({}).test) --> nil
If you really want to get the nil, you could use a dummy value:
local NIL = setmetatable({},{__tostring=function() return "nil" end})
an_func = function(x) return x == NIL end
for num, element in pairs({'None', NIL, 1, '1'}) do
print(num .. ': ' .. tostring(an_func(element)))
end
Output:
1: false
2: true
3: false
4: false
In Lua, everything (a function, string, thread, ...) is a value, with nil (and technically none/void) being the only exception(s). To put simply, nil represents the absence of value. So when iterating over a table, why would Lua say there is nothing there. If it did it would have to iterate over every virtually possible index you can create.
However it appears you are using an array. You technically can iterate over it assuming you know the length. I'll make the assumption that the last item in the table is NOT nil, and that there wasn't any holes assigned after the initial length of the table with holes, however this may not necessarily be true in your case, if it isn't you will have to keep track of the length yourself and use it rather than the # operator:
for i = 1,#t do
local v = t[i]
print (v == nil)
end
Other answers address your second question well but the simple explanatory facts are Any key with value nil is not considered part of the table. Conversely, any key that is not part of a table has an associated value nil. Assigning a value of nil removes any existing key-value pair with the same key.
You would need to loop through all keys you want to print the values for.
Possible ways:
Know the length beforehand. (warspyking noted about this)
Using an end marker.
Calculate the length somehow (for example with maxn).
Use something to represent nil values. (EinsteinK showed an example
of this)
Each has their good and bad sides, here are some:
For the first you would need to know the length beforehand in all cases which may be difficult.
The end marker is convenient, but requires you to shift around the end marker.
Calculating length would not work if the last element is nil.
Example of calculating the length - assuming last element is not nil.
Note that lua 5.1 has table.maxn that you can use instead of this custom implementation of maxn.
an_func = function(x) return x == nil end
function maxn(t)
local n = 0
for k,v in pairs(t) do
if k%1 == 0 then
n = k
end
end
return n
end
local t = {'None', nil, 1, '1'}
for num = 1, maxn(t) do
print(num .. ': ' .. tostring(an_func(t[num])))
end
Example of using an end marker:
an_func = function(x) return x == nil end
local END = {}
local t = {'None', nil, 1, '1', END}
local num = 1
while t[num] ~= END do
print(num .. ': ' .. tostring(an_func(t[num])))
num = num+1
end

Lua in pairs with same order as it's written

Is there any way to loop trough a table like the one below in the same order as it's written?
local tbl = {
["hello"] = 1,
[2] = 2,
[50] = 3,
["bye"] = 4,
[200] = 5
}
What I mean is that when I use "in pairs" I'll get a different order everytime I execute my code ...
I'm searching for something like this:
function get_keys(tbl)
local rtable = {}
for k,v in pairs(tbl) do
table.insert(rtable, k)
end
return rtable
end
local keys_of_tbl = get_keys(tbl)
for i = 1, table.getn(keys_of_tbl) do
--Do something with: tbl[keys_of_tbl[i]]
end
But because the function "get_keys" is based on "in pairs" again, it won't work ...
In Lua, the order that pairs iterates through the keys is unspecified. However you can save the order in which items are added in an array-style table and use ipairs (which has a defined order for iterating keys in an array). To help with that you can create your own ordered table using metatables so that the key order will be maintained when new keys are added.
EDIT (earlier code inserted multiple copies of the key on updates)
To do this you can use __newindex which we be called so long as the index is not added yet to the table. The ordered_add method updates, deletes, or stores the key in the hidden tables _keys and _values. Note that __newindex will always be called when we update the key too since we didn't store the value in the table but instead stored it in the "hidden" tables _keys and _values.
Note however that we cannot use any key in this table, the key name "_keys" will overwrite our hidden table so the safer alternative is to use the ordered_table.insert(t, key, value) ordered_table.index(t, key) and ordered_table.remove(t, key) methods.
ordered_table = {}
function ordered_table.insert(t, k, v)
if not rawget(t._values, k) then -- new key
t._keys[#t._keys + 1] = k
end
if v == nil then -- delete key too.
ordered_table.remove(t, k)
else -- update/store value
t._values[k] = v
end
end
local function find(t, value)
for i,v in ipairs(t) do
if v == value then
return i
end
end
end
function ordered_table.remove(t, k)
local v = t._values[k]
if v ~= nil then
table.remove(t._keys, find(t._keys, k))
t._values[k] = nil
end
return v
end
function ordered_table.index(t, k)
return rawget(t._values, k)
end
function ordered_table.pairs(t)
local i = 0
return function()
i = i + 1
local key = t._keys[i]
if key ~= nil then
return key, t._values[key]
end
end
end
function ordered_table.new(init)
init = init or {}
local t = {_keys={}, _values={}}
local n = #init
if n % 2 ~= 0 then
error"in ordered_table initialization: key is missing value"
end
for i=1,n/2 do
local k = init[i * 2 - 1]
local v = init[i * 2]
if t._values[k] ~= nil then
error("duplicate key:"..k)
end
t._keys[#t._keys + 1] = k
t._values[k] = v
end
return setmetatable(t,
{__newindex=ordered_table.insert,
__len=function(t) return #t._keys end,
__pairs=ordered_table.pairs,
__index=t._values
})
end
--- Example Usage:
local t = ordered_table.new{
"hello", 1, -- key, value pairs
2, 2,
50, 3,
"bye", 4,
200, 5
}
print(#t)
print("hello is", t.hello)
print()
for k, v in pairs(t) do --- Lua 5.2 __pairs metamethod
print(k, v)
end
t.bye = nil -- delete that
t[2] = 7 -- use integer keys
print(#t)
No. There's no "as written in the source" order to tables. (Consider that not all keys necessarily exist in the source.) lua has no concept of "in order" for non-contiguous integer keys.
If you want a specific order you get to keep that order yourself manually in some way.
If you don't have any integer keys in your table then you can use those as your order (and use ipairs to loop those keys and index the value as the key to get the real value).
If your original values are the order you want to sort in then you can loop and reverse map to get a table that you can iterate with ipairs once done.

How to remove all 'nil' values from an array?

I have an array of objects (or just numbers), and I have another array which contains all the objects that should not be removed from the first array in any circumstances. It looks something like this:
-- Array of objects (just numbers for now)
Objects = {}
-- Array of objects that should always stay in the 'Objects' array
DontDestroyThese = {}
-- Populate the arrays
Objects[#Objects+1] = 1
Objects[#Objects+1] = 2
Objects[#Objects+1] = 3
Objects[#Objects+1] = 4
Objects[#Objects+1] = 5
DontDestroyThese[#DontDestroyThese+1] = 2
DontDestroyThese[#DontDestroyThese+1] = 5
Now, I have a method called destroy() that should remove all objects from the Objects array except those included in the DontDestroyThese array. The method looks something like this:
function destroy()
for I = 1, #Objects do
if(DontDestroyThese[Objects[I]] ~= nil) then
print("Skipping " .. Objects[I])
else
Objects[I] = nil
end
end
end
However, as the result, the Objects array now contains nil values here and there. I'd like to remove these nils so that the Objects array would consist only of the numbers that were left there after calling destroy(). How do I do that?
The most efficient way is probably to create a new table to hold the result. Trying to move values around in the array is likely to have a higher overhead than simply appending to a new table:
function destroy()
local tbl = {}
for I = 1, #Objects do
if(DontDestroyThese[Objects[I]] ~= nil) then
table.insert(tbl, Objects[I])
end
end
Objects = tbl
end
This method also means you don't have to deal with altering the contents of the table/array you're iterating over.
I think the solution is much simpler. To remove any nils ('holes' in your array), all you need to do is iterate your table using pairs(). This will skip over any nils returning only the non-nil values that you add to a new local table that is returned in the end of the 'cleanup' function. Arrays (tables with indices from 1..n) will remain with the same order. For example:
function CleanNils(t)
local ans = {}
for _,v in pairs(t) do
ans[ #ans+1 ] = v
end
return ans
end
Then you simply need to do this:
Objects = CleanNils(Objects)
To test it:
function show(t)
for _,v in ipairs(t) do
print(v)
end
print(('='):rep(20))
end
t = {'a','b','c','d','e','f'}
t[4] = nil --create a 'hole' at 'd'
show(t) --> a b c
t = CleanNils(t) --remove the 'hole'
show(t) --> a b c e f
local function remove(t, pred)
for i = #t, 1, -1 do
if pred(t[i], i) then
table.remove(t, i)
end
end
return t
end
local function even(v)
return math.mod(v, 2) == 0
end
-- remove only even numbers
local t = remove({1, 2, 3, 4}, even)
-- remove values you want
local function keep(t)
return function(v)
return not t[v]
end
end
remove(Objects, keep(DontDestroyThese))

How can I check if a lua table contains only sequential numeric indices?

How can I write a function that determines whether it's table argument is a true array?
isArray({1, 2, 4, 8, 16}) -> true
isArray({1, "two", 3, 4, 5}) -> true
isArray({1, [3]="two", [2]=3, 4, 5}) -> true
isArray({1, dictionaryKey = "not an array", 3, 4, 5}) -> false
I can't see any way of finding out if the numeric keys are the only keys.
EDIT: Here's a new way to test for arrays that I discovered just recently. For each element returned by pairs, it simply checks that the nth item on it is not nil. As far as I know, this is the fastest and most elegant way to test for array-ness.
local function isArray(t)
local i = 0
for _ in pairs(t) do
i = i + 1
if t[i] == nil then return false end
end
return true
end
ipairs iterates over indices 1..n, where n+1 is the first integer index with a nil value
pairs iterates over all keys.
if there are more keys than there are sequential indices, then it cannot be an array.
So all you have to do is see if the number of elements in pairs(table) is equal to the number of elements in ipairs(table)
the code can be written as follows:
function isArray(tbl)
local numKeys = 0
for _, _ in pairs(tbl) do
numKeys = numKeys+1
end
local numIndices = 0
for _, _ in ipairs(tbl) do
numIndices = numIndices+1
end
return numKeys == numIndices
end
I'm pretty new to Lua, so there might be some builtin function to reduce the numKeys and numIndices calculations to simple function calls.
By "true array", I suppose you mean a table whose keys are only numbers. To do this, check the type of every key of your table. Try this :
function isArray(array)
for k, _ in pairs(array) do
if type(k) ~= "number" then
return false
end
end
return true --Found nothing but numbers !
end
Note: as #eric points out, pairs is not defined to iterate in a specific order. Hence this is no valid answer.
The following should be sufficient; it checks that the keys are sequential from 1 until the end:
local function isArray(array)
local n = 1
for k, _ in pairs(array) do
if k ~= n then return false end
n = n + 1
end
return true
end
I wrote this code for another similar question lately:
---Checks if a table is used as an array. That is: the keys start with one and are sequential numbers
-- #param t table
-- #return nil,error string if t is not a table
-- #return true/false if t is an array/isn't an array
-- NOTE: it returns true for an empty table
function isArray(t)
if type(t)~="table" then return nil,"Argument is not a table! It is: "..type(t) end
--check if all the table keys are numerical and count their number
local count=0
for k,v in pairs(t) do
if type(k)~="number" then return false else count=count+1 end
end
--all keys are numerical. now let's see if they are sequential and start with 1
for i=1,count do
--Hint: the VALUE might be "nil", in that case "not t[i]" isn't enough, that's why we check the type
if not t[i] and type(t[i])~="nil" then return false end
end
return true
end
Here's my take on this, using #array to detect a gap or stop when too many keys have been read:
function isArray(array)
local count=0
for k,_ in pairs(array) do
count=count+1
if (type(k) ~= "number" or k < 1 or k > #array or count > #array or math.floor(k) ~= k) then
return false
end
end
if count ~= #array then
return false
end
return true
end
Iterate from 0 to the number of elements, and check if all elements with the counter's index exist. If it's not an array, some indexes will miss in the sequence.

Resources