Comparing two index tables by index-value in lua - lua

I'm attempting to compare two tables of equal length with a function, since I don't know of any other way to do so. However, with the following function, it fails to register, and I've no clue why. I'm hoping someone can provide insight to this problem or has a better way of comparing the two tables.
The tables are being populated with the following code:
str = "parameters determined by program (all digits)"
tableone = {}
for word in str:gmatch("%d") do table.insert(tableone,word) end
It's identical for both tables, except, of course, the individual table names. The tables are being populated properly, and display properly when I print them. Here are two tables for the sake of this question:
tableone = {}
tabletwo = {}
for i=1,4 do table.insert(tableone, i) end
for i=1,4 do table.insert(tabletwo, i) end
Obviously, these two tables are going to be equal to each other. The function I wrote to compare the index tables is as follows:
function comparetables(t1, t2)
matchct = 0
for i=1,#t1 do
if t1[i] == t2[i] then
matchct = matchct + 1
end
if matchct == #t1 then
return true
end
end
I tried doing
print(comparetables(tableone,tabletwo))
to see if it'll print "true" but no luck. To me, it seems like it should work without a problem. Yet it doesn't. What am I missing? I've tried searching for something like a table.compare function that someone may have already written, but no such luck in finding one. Thanks for any suggestions!
Additional information:
The reason I'm comparing tables is for a mastermaind-type game. That means the following three rules must apply when comparing tables. The function I created was to just get me started, thinking I could work from there.
When comparing the tables, if the numbers match, Ccount increases by 1.
When comparing tables, if the value exists in a different index position, increment Pcount by 1
For example, with a table of values {1, 3, 3, 4} and a guess of {4, 4, 3, 1}, it would return Pcount of 2 (the one 4 and the 1) and a Ccount of 1 (the three in the third position). I think one of the hardest parts is going to be getting the comparison to recognize that the second 4 in the guess should not increment the Pcount at all.

A slight variant on your code that should work is:
function comparetables(t1, t2)
if #t1 ~= #t2 then return false end
for i=1,#t1 do
if t1[i] ~= t2[i] then return false end
end
return true
end
However I use something more like this: It checks the types of the arguments, their metatables, and a few other cases.
-- This is not clever enough to find matching table keys
-- i.e. this will return false
-- recursive_compare( { [{}]:1 }, { [{}]:1 } )
-- but this is unusual enough for me not to care ;)
-- It can also get stuck in infinite loops if you use it on
-- an evil table like this:
-- t = {}
-- t[1] = t
function recursive_compare(t1,t2)
-- Use usual comparison first.
if t1==t2 then return true end
-- We only support non-default behavior for tables
if (type(t1)~="table") then return false end
-- They better have the same metatables
local mt1 = getmetatable(t1)
local mt2 = getmetatable(t2)
if( not recursive_compare(mt1,mt2) ) then return false end
-- Check each key-value pair
-- We have to do this both ways in case we miss some.
-- TODO: Could probably be smarter and not check those we've
-- already checked though!
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if( not recursive_compare(v1,v2) ) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if( not recursive_compare(v1,v2) ) then return false end
end
return true
end
Here's an example of it in use:
print( recursive_compare( {1,2,3,{1,2,1}}, {1,2,3,{1,2,1}} ) ) -- prints true
print( recursive_compare( {1,2,3,{1,2,1}}, {2,2,3,{1,2,3}} ) ) -- prints false

If you're comparing objects that are more objecty than tabley in an Object oriented sense, then I'd look at implementing the functions in the lua OO way.
Something like this should do the trick:
GameState = {}
GameState.mt = {}
GameState.mt.fns = {}
GameState.mt.__index = GameState.mt.fns
function GameState.new(a,b,c,d)
-- TODO: put argument checks here...
local retval = {}
retval[1] = a
retval[2] = b
retval[3] = c
retval[4] = d
setmetatable(retval, GameState.mt)
return retval
end
function GameState.mt.fns.print( self )
print(" GameState: ", self[1], self[2], self[3], self[4] )
end
function GameState.mt.__tostring( self )
return "GameState: "..self[1].." "..self[2].." "..self[3].." "..self[4]
end
function GameState.mt.__eq(self, other)
-- Check it's actually a GameState, and all its bits match
return getmetatable(other)==GameState.mt and
(self[1] == other[1]) and
(self[2] == other[2]) and
(self[3] == other[3]) and
(self[4] == other[4])
end
Then you'd use it like this:
state1 = GameState.new(1,2,3,4)
state2 = GameState.new(1,2,3,4)
print("State 1 is:")
state1:print()
print("State 2 is:")
print(state2)
print( "state1 == state2 : ", state1 == state2 )
print( "Changing state 2")
state2[1]=2
print( "state1 == state2 : ", state1 == state2 )

Related

Bubble Sort algorithm in Lua will not work

I am currently learning Lua, and I am attempting to create a bubble sort algorithm with it. However, I am unable to get this to work. Is anyone able to point out why?
Some details I can point out are that, on shorter lists such as lists 3 elements long, the algorithm does succeed in sorting it but then continues to sort it as if it was not sorted. On longer lists such as lists 5 elements long, the program does not sort it at all. I have gotten this information by making the program print the list every time two elements are swapped.
user_input = 0
list = {}
while user_input ~= "SORT" do
print("Input number value, or input SORT to start sort")
user_input = io.read()
if user_input ~= "SORT" then
table.insert(list, user_input)
end
end
done = false
while done == false do
done = true
for k, v in pairs(list) do
if k ~= 1 then
if list[k] < list[k - 1] then
list[k], list[k - 1] = list[k - 1], list[k]
done = false
for k, v in pairs(list) do
io.write(v .. " ")
end
print()
end
end
if k == 1 then
if list[k] < list[table.maxn(list)] then
list[k], list[table.maxn(list)] = list[table.maxn(list)], list[k]
done = false
for k, v in pairs(list) do
io.write(v .. " ")
end
print()
end
end
end
end
io.write("RESULT: ")
for k, v in pairs(list) do
io.write(v .. " ")
end
print()
In Lua there are 2 functions for creating iterators for tables pairs and ipairs
With pairs the order produced is indeterminate, it will returned all key, value pairs but the order can not be garenteed.
You can find this under next in the reference manual for Lua 5.1
example: {1,2,3,4,5} the output could be 5,2,4,1,3
the order of ipairs is 1 to the first nil it will not return any non-integer keys.
example: {1,2,3,nil,5} would give you 1,2,3
Using pairs your algorithm's output could be correct but could appear incorrect due to the ordering from pairs
You want you use ipairs to evaluate your algorithm's output, so that it will be ordered by index.
Your algorithm does not perform a bubble sort as it is now, I can include corrections for that if you want. For this initial answer I think clearing up what would create an inconsistent output should point you in the right direction.
A bubble sort should "sort" 1 index at a time, the first pass will get the last value of the array sorted. then each pass after sorts following position. for example:
before first pass 2,1,3,4,5,8,7,6
after first pass 1,2,3,4,5,7,6,8
Wikipedia has great pages for sorting algorithms with gif images that can really help understand how it should function: Bubble Sort
You're using pairs() instead of ipairs()
plus your code has an extra maxn() routine that isn't needed.
#! /usr/bin/env lua
local user_input = 0
local list = {}
while user_input ~= 'sort' do
print( 'Input number value, or input SORT to start sort' )
user_input = io.read() :lower()
-- the word 'sort' doesn't return a number, thus no final insertion
table.insert( list, tonumber( user_input ) )
end
local found_swap = true
while found_swap do
found_swap = false
for i = 2, #list do
if list[i] < list[i -1] then
list[i], list[i -1] = list[i -1], list[i]
found_swap = true
end
end
end
io.write( 'RESULT: ' )
for i, v in ipairs( list ) do
io.write( v ..' ' )
end
print()
here's my implementation, a couple notes:
My version of lua (luau) supports type checking etc. so you may need to remove : table from the paramater. (If you'd like to find more out about luau - here's the link: https://roblox.github.io/luau/ - it's really cool)
local function bubbleSort(t: table)
local function check(i, v)
local nextNum = t[i+1]
if nextNum and (nextNum < v) then
t[i] = nextNum
t[i + 1] = v
return true
end
end
local changeMade
repeat
changeMade = false
for i, v in next, t do
changeMade = check(i, v) or changeMade
end
until changeMade == false
end
local tableForSorting = {2,5,4} --2,4,5
bubbleSort(tableForSorting)
print(tableForSorting)
Hope this is of some help and shows how you can really slim down your codebase and it still work exactly the same! The reason yours wasn't working - as others - have mentioned is pairs() doesn't consistently order the list the same way.
In practise we use pairs() for dictionaries while we use ipairs() for arrays. You'll notice i'm using next in this example, next is exactly the same as ipairs(). Drop a question below if you have any follow up questions :)!

Count number of occurrences of a value in Lua table

Basically what I want to do is convert a table of this format
result={{id="abcd",dmg=1},{id="abcd",dmg=1},{id="abcd",dmg=1}}
to a table of this format:
result={{id="abcd",dmg=1, qty=3}}
so I need to know how many times does {id="abcd",dmg=1} occur in the table. Does anybody know a better way of doing this than just nested for loops?
result={{id="abcd",dmg=1},{id="defg",dmg=2},{id="abcd",dmg=1},{id="abcd",dmg=1}}
local t, old_result = {}, result
result = {}
for _, v in ipairs(old_result) do
local h = v.id..'\0'..v.dmg
v = t[h] or table.insert(result, v) or v
t[h], v.qty = v, (v.qty or 0) + 1
end
-- result: {{id="abcd",dmg=1,qty=3},{id="defg",dmg=2,qty=1}}
So you want to clear duplicate contents, although a better solution is to not let dupe contents in, here you go:
function Originals(parent)
local originals = {}
for i,object in ipairs(parent) do
for ii,orig in ipairs(originals) do
local dupe = true
for key, val in pairs(object) do
if val ~= orig[key] then
dupe = false
break
end
end
if not dupe then
originals[#originals+1] = object
end
end
return originals
end
I tried to make the code self explanatory, but the general idea is that it loops through and puts all the objects with new contents aside, and returns them after.
Warning: Code Untested

Different methods for determining whether a number is included in a given list

Is it faster to check whether a number is equivalent to another, or to look up the number in a table?
I have a program where commands sent from a server are received as numbers. Commands are sent frequently: anywhere from 1/second to 30+/second. Certain commands are ignored while others trigger various events. Would it be better to determine which event to trigger by doing this:
function incoming_integer(n)
if n == 33 then
print('a')
elseif n == 44 then
print('b')
elseif n == 567 then
print('c')
... (the actual list is occasionally pretty long: upwards of ten valid numbers)
end
end
or this:
functions = {
[33] = function() print('a') end,
[44] = function() print('b') end,
[567] = function() print('c') end,
...
}
relevant_commands = {[33]= true, [44]=true, [567]=true ...}
function incoming_integer(n)
if relevant_commands[n] then
functions[n]()
end
end
Is there a point at which one method becomes more efficient?
What if the commands were instead sent as strings?
For your throughput, I would suggest that the overhead for either mechanism is likely to be negligible.
I ran very quick and dirty test of the overhead of the table lookup for both string and numeric keys. See below. Even with a large number of keys and a high hit rate, I struggled to get >1us overhead even on the slowest box I could find.
f = {}
function incoming_integer(n)
local t = f[n]
return t and t()
end
SIZE = 10000
RAN = 20000
REPEAT = 10000000
for i =1,SIZE do
--f[math.random(RAN)] = function() return "z" end
f[tostring(math.random(RAN))] = function() return "z" end
end
tm = os.time()
for i = 1,REPEAT do
--math.random(RAN)
tostring(math.random(RAN))
end
print(os.difftime(os.time(),tm))
tm = os.time()
for i = 1,REPEAT do
--incoming_integer(math.random(RAN))
incoming_integer(tostring(math.random(RAN)))
end
print(os.difftime(os.time(),tm))
If you want to trigger different events with the returned numbers, I would use this way...
function trigger( value )
if ( value == "..." ) then
triggerThis()
elseif ( value == "..." ) then
triggerElse()
-- and so on
end
end
function incoming_integer(n)
if ( n ) then -- check if variable is incoming to prevent errors
trigger( n )
else
alert( "noop, there is nothing here" )
end
end
Maybe you could try keys. Didn't tested it just an example.
http://lua-users.org/wiki/TablesTutorial
table = { [33]="a", [44]="b", [567]="c" }
function onCall( value ) -- if 33
if ( table[value] ) then
return( table[value] ) -- then if exists return a
end
end
onCall( 33 ) -- now it will search in table and get key
EDIT: You could name the triggered functions like the number, so you can directly trigger them. This would save a lot of time!
functions = {[33] = function() print('a') end, etc...}
setmetatable(functions, {__index = function() return function() end end}) -- return empty function if no defined function is found
Usage:
functions[32]() -- ignores
functions[33]() -- does defined function

lua - checking for duplicate data in a string

I have the following string data that I receive as input:
"route1,1234,1,no~,,route2,1234,1,no~,"
It represents two "records" of data... where each record has 4 fields.
I've built code to parse this string into it's individual columns / fields.
But the part that isn't working is when I test to see if I have any duplicates in field 2. Field 2 is the one that currently has "1234" as the value.
Here's the code:
function string:split(delimiter)
local result = { }
local from = 1
local delim_from, delim_to = string.find( self, delimiter, from )
while delim_from do
table.insert( result, string.sub( self, from , delim_from-1 ) )
from = delim_to + 1
delim_from, delim_to = string.find( self, delimiter, from )
end
table.insert( result, string.sub( self, from ) )
return result
end
local check_for_duplicate_entries = function(route_data)
local route
local route_detail = {}
local result =true
local errtxt
local duplicate = false
print("received :" ..route_data)
route = string.gsub(route_data, "~,,", "~")
route = route:sub(1,string.len(route)-2)
print("route :" ..route)
-- break up in to an array
route = string.split(route,"~")
for key, value in pairs(route) do
route_detail[key] = string.split(value,",")
end
local list_of_second_column_only = {}
for key,value in pairs(route_detail) do
local temp = value[2]
print(temp .. " - is the value I'm checking for")
if list_of_second_column_only[temp] == nil then
print("i dont think it exists")
list_of_second_column_only[key] = value[2]
print(list_of_second_column_only[key])
else
--found a duplicate.
return true
end
end
return false
end
print(check_for_duplicate_entries("route1,1234,1,no~,,route2,1234,1,no~,"))
I think where I'm going wrong is the test:
if list_of_second_column_only[temp] == nil then
I think I'm checking for key with the value temp instead of a value with the value that temp contains. But I don't know how to fix the syntax.
Also, I'm wondering if there's a more efficient way to do this.
The number of "records" i receive as input is dynamic / unknown, as is the value of the second column in each record.
Thanks.
EDIT 1
The post I was trying to use as a reference is: Search for an item in a Lua list
In the answer, they show how to test for a record in the table by value, instead of looping through the entire table...
if items["orange"] then
-- do something
end
I was playing around to try and do something similar...
This should be a bit more efficient with only one table creation and less regex matching.
The match does require that you're only interested in dups in the second field.
local function check_for_duplicate_entries(route_data)
assert(type(route_data)=="string")
local field_set = {}
for route in route_data:gmatch"([^~]*)~,?,?" do
local field = route:match",([^,]*)"
if field_set[field] then
return true
else
field_set[field] = true
end
end
return false
end
Try this. It's doing the check on the value of the second field.
I haven't looked at the efficiency.
if list_of_second_column_only[value[2]] == nil then
print("i dont think it exists")
list_of_second_column_only[value[2]] = true
print(list_of_second_column_only[value[2]])
else
--found a duplicate.
return true
end

Trying to compare all entries of one table in Lua

Basically I have a table of objects, each of these objects has one particular field that is a number. I'm trying to see if any of these numerical entries match up and I can't think of a way to do it. I thought possibly a double for loop, one loop iterating through the table, the other decrementing, but won't this at some point lead to two values being compared twice? I'm worried that it may appear to work on the surface but actually have subtle errors. This is how I pictured the code:
for i = #table, 1, -1 do
for j = 1, #table do
if( table[i].n == table[j].n ) then
table.insert(table2, table[i])
table.insert(table2, table[j])
end
end
end
I want to insert the selected objects, as tables, into another pre made one without any duplicates.
Let the outer loop run over the table, and let the inner loop always start one element ahead of the outer one - this avoids double counting and comparing objects with themselves. Also, if you call the table you want to examine table that will probably hide the table library in which you want to access insert. So let's say you call your input table t:
for i = 1, #t do
for j = i+1, #t do
if( t[i].n == t[j].n ) then
table.insert(table2, t[i])
table.insert(table2, t[j])
end
end
end
Still, if three or more elements have the same value n you will add some of them multiple times. You could use another table to remember which elements you've already inserted:
local done = {}
for i = 1, #t do
for j = i+1, #t do
if( t[i].n == t[j].n ) then
if not done[i] then
table.insert(table2, t[i])
done[i] = true
end
if not done[j] then
table.insert(table2, t[j])
done[j] = true
end
end
end
end
I admit this isn't really elegant, but it's getting late over here, and my brain refuses to think of a neater approach.
EDIT: In fact... using another table, you can reduce this to a single loop. When you encounter a new n you add a new value at n as the key into your helper table - the value will be that t[i] you were just analysing. If you encounter an n that already is in the table, you take that saved element and the current one and add them both to your target list - you also replace the element in the auxiliary table with true or something that's not a table:
local temp = {}
for i = 1, #t do
local n = t[i].n
if not temp[n] then
temp[n] = t[i]
else
if type(temp[n]) == "table" then
table.insert(table2, temp[n])
temp[n] = true
end
table.insert(table2, t[i])
end
end

Resources