Table Comparison

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
vynom
Prole
Posts: 28
Joined: Sat Oct 05, 2013 4:42 pm
Location: Teh Interwebz
Contact:

Table Comparison

Post by vynom »

Hey all,
This is more of a Lua question than LOVE. However, I am trying to compare tables, and have tried a few different ways, each with varying levels of success. End result, I want to check that table qa.entity.materials has all the required values of qa.ship.materials, but also if entity has the material[1] it also checks the second value in the table to make sure the amount in qa.entity.materials[2] is higher. I'll show you what I have here:
This is a hardcode version for testing, copy paste here to see what I mean.

Code: Select all

local qa = {ship = {}, entity = {}}
qa.ship.materials = {{10, 50}, {25, 100}, {95, 22}}
qa.entity.materials =  {{10, 50}, {25, 100}, {95, 22}}
function qa.checkMaterials()
  for i=1,#qa.ship.materials do
    for _,v in pairs(qa.entity.materials) do
      if qa.ship.materials[i][1] == v[1] then
        if qa.ship.materials[i][2] > v[2] then
          return false
        else
          print("Passed")
        end
      end
    end
  end
return true
end
if qa.checkMaterials() then print("All Clear") else print("Failed") end
It looks like it works at this point, but if you change qa.entity.materials to {{10, 50}}, it still passes. Since one value did match, even though the remaining tables in qa.ship.materials has unchecked values. I checked some more advanced table comparison topics(here), but I cant seem to come to a solution that works well. Is there a way to check in either for loop that if the value in qa.ship.materials[1] does not match one within qa.entity.materials, if returns false but if true continues checking? Let me know if clarification is needed, but I could really use some help, on what I assume to be a trivial issue lol


Edit>> Forgot to mention, I did come up with a gritty way of doing it and that was by assigning another value to the array so it would not be nil, then looping through the list one more time, and returning false is any of the [3] were nil. But this way seems like the long way
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Table Comparison

Post by s-ol »

Check out the "__eq" metamethod; it gets two tables and compares them. You can overwrite that with your custom comparison (return l.material == r.material and l.x = r.x ....) and then just use table1 == table2.

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
User avatar
Positive07
Party member
Posts: 1014
Joined: Sun Aug 12, 2012 4:34 pm
Location: Argentina

Re: Table Comparison

Post by Positive07 »

S0lll0s wrote:Check out the "__eq" metamethod; it gets two tables and compares them. You can overwrite that with your custom comparison (return l.material == r.material and l.x = r.x ....) and then just use table1 == table2.
Did you even read the issue? this is totally unrelated!

To solve your poblems you could have a flag called passed for each entry in qa.ship.materials, this means a local variable in the first loop, which you only set to true if that material passes (that is, the material exists in qa.entity.materials, the material quantity is less than the max quantity) then if after the second loop your flag isnt true then you should error

Here is the code to do this

Code: Select all

local qa = {ship = {}, entity = {}}
qa.ship.materials = {{10, 50}, }
qa.entity.materials =  {{10, 50}, {25, 100}, {95, 22}}
function qa.checkMaterials()
  for i=1,#qa.ship.materials do
    local passed = false
    for _,v in pairs(qa.entity.materials) do
      if qa.ship.materials[i][1] == v[1] then
        if qa.ship.materials[i][2] <= v[2] then
          print("Passed")
          passed = true
        end
      end
    end
    if not passed then return false end
  end
  return true
end
if qa.checkMaterials() then print("All Clear") else print("Failed") end
Alternatively you could change the structure of the table qa.entity.materials, so the element type (the first number) is the key, while the max quantity is the value

Here is the same result:

Code: Select all

local qa = {ship = {}, entity = {}}
qa.ship.materials = {{10, 50}, {25, 100}, {95, 22}}
qa.entity.materials =  {[10]=50, [25]=100, [95]=22}
function qa.checkMaterials()
  for i=1,#qa.ship.materials do
    local value = qa.entity.materials[qa.ship.materials[i][1]]
    if value and value >= qa.ship.materials[i][2] then
       print("Passed")
    else
       return false
    end
  end
  return true
end
if qa.checkMaterials() then print("All Clear") else print("Failed") end
I recommend the second choice, since you can get rid of a loop, also since you probably dont want to have two materials with the same name but different max values this should work regardless of the data, the only problem being that you need to change the structure of your table, and probably traverse it with pairs instead of ipairs...
for i, person in ipairs(everybody) do
[tab]if not person.obey then person:setObey(true) end
end
love.system.openURL(github.com/pablomayobre)
User avatar
vynom
Prole
Posts: 28
Joined: Sat Oct 05, 2013 4:42 pm
Location: Teh Interwebz
Contact:

Re: Table Comparison

Post by vynom »

Thank you for the reply Positive07, the first was similar to what I was able to get working, but in a much cleaner(and assumingly faster) way. The second gets rid of an extra loop, and is very efficient. I'll have to consider the table structure and feasibility in the long term using the second method, but it clears an extra loop. Thanks again!

EDIT>> Second method works wonderful, in multiple places! For instances where there may be multiple "piles" of materials of the same id, theres an extra loop to go through each index value to see if they add up to the right amount, if that makes sense. I may add a separate function that combines them pre-comparison, see if that speeds things up a tad, but it still works wonders!

Double EDIT>> Forgot to share how I converted it to work across the board with tables(extra "piles" check will come later if needed)

Code: Select all

local qa = {ship = {}, entity = {}}
qa.ship.materials = { [25]=100,[10]=50, [95]=22}
qa.entity.materials =  {[10]=50, [25]=100, [95]=22}
function qa.checkMaterials()
  for i in pairs(qa.ship.materials) do
    local value = qa.entity.materials[i]
    if value and value >= qa.ship.materials[i] then
       print("Passed")
    else
       return false
    end
  end
  return true
end
if qa.checkMaterials() then print("All Clear") else print("Failed") end
User avatar
Positive07
Party member
Posts: 1014
Joined: Sun Aug 12, 2012 4:34 pm
Location: Argentina

Re: Table Comparison

Post by Positive07 »

vynom wrote:-snip-
Great! I'm glad I could help.

NOTE: You could use the second argument returned by pairs, the value, so that your code looks like this

Code: Select all

local qa = {ship = {}, entity = {}}
qa.ship.materials = { [25]=100,[10]=50, [95]=22}
qa.entity.materials =  {[10]=50, [25]=100, [95]=22}
function qa.checkMaterials()
  for k,v in pairs(qa.ship.materials) do
    local value = qa.entity.materials[k]
    if value and value >= v then
       print("Passed")
    else
       return false
    end
  end
  return true
end
if qa.checkMaterials() then print("All Clear") else print("Failed") end
You win little, but is cleaner (maybe?). Also I see you have restructured both tables, that is nice because you make sure that no item appears twice, but I must advice that pairs is slower than a normal traverse or ipairs, but you shouldnt really bother about this stuff, because optimization is the root of all evil!
for i, person in ipairs(everybody) do
[tab]if not person.obey then person:setObey(true) end
end
love.system.openURL(github.com/pablomayobre)
Post Reply

Who is online

Users browsing this forum: Amazon [Bot], Semrush [Bot] and 7 guests