Page 1 of 1

Weird SE_DeleteUnit()

Posted: Sun May 08, 2022 6:34 pm
by vettim89
Pardon the extraneous 'prints' in this code as I was trying to parse through the lines

Code: Select all

local b=ScenEdit_GetUnit({name='Banak [Diversionary Air Base]', guid='W7ZXL3-0HM0RFF6Q2498'})
print(#b.hostedUnits['Aircraft'])
for i=1,#b.hostedUnits['Aircraft'] do
local g=b.hostedUnits['Aircraft'][i]
print(g)
local u=ScenEdit_GetUnit({guid=g})
ScenEdit_DeleteUnit({guid=u.guid})
end
So when I run that it only deletes aircraft #1,3,5,&7. Aircraft #2,4,6,&8 are remaining and the console is giving me this error: 'ScenEdit_GetUnit 0 : 'Need to define a Name or Guid to identify a unit. Preferably a Guid or Side & Name.' Why is the code skipping/having a problem with the even numbers?

Re: Weird SE_DeleteUnit()

Posted: Sun May 08, 2022 9:10 pm
by nukkxx5058
It's because when you delete the aircraft, your i counter skips one number.

i= 1 --> delete aircraft i=1
i= 2 --> delete aircraft i=2 BUT it's aircraft #2 in the new list after having deleted the first aircraft. So for your second loop, you are skipping the new aircraft #1 (after deletion)

Hope I'm clear enough :-)

Re: Weird SE_DeleteUnit()

Posted: Mon May 09, 2022 12:04 am
by vettim89
Of course! That makes perfect sense now that your point it out. Can you make a 'for' loop counter backwards?

Perhaps I need to use a do/while loop instead

Re: Weird SE_DeleteUnit()

Posted: Mon May 09, 2022 1:46 am
by KnightHawk75
vettim89 wrote: Mon May 09, 2022 12:04 am Of course! That makes perfect sense now that your point it out. Can you make a 'for' loop counter backwards?

Perhaps I need to use a do/while loop instead
No you should not use a do\while.
You can use a decrementing for if you want and it will technically address your specific issue in this specific case, however it may not work for you in other cases. How to go backwards: for i=#sometable,1,-1 do print(i); end

The issue is you are indirectly making deletes to the object being looped during the loop which changes things on each iteration. Don't if you can avoid it. Often you will want to either deep-copy the table first, or run through it grabbing what you need first, and then a second time to do the actions.

--using decrementing for loop which will technically work for this very specific example.

Code: Select all

function DeleteAllHostedUnits1(u)
  if ((u ~= nil) and u.hostedUnits ~=nil) and u.hostedUnits.Aircraft ~=nil then
    local tmptable = u.hostedUnits.Aircraft  -- make a copy as the original may be reevaluated\updates during the for.
    for i=#u.hostedUnits.Aircraft,1,-1 do
     --There is no need to get each unit, unless doing something else first, line removed.
      print('deleting unit ' .. i .. ' with guid ' .. u.hostedUnits.Aircraft[i] .. ' that was hosted on ' .. u.name)
      pcall(ScenEdit_DeleteUnit,{guid=u.hostedUnits.Aircraft[i]});
    end
  end
end

DeleteAllHostedUnits1(ScenEdit_GetUnit({name='SUA1', guid='4FH7PU-0HMHHATCAVSIG'}));
--using simple copy by value first.

Code: Select all

function DeleteAllHostedUnits2(u)
  if ((u ~= nil) and u.hostedUnits ~=nil) and u.hostedUnits.Aircraft ~=nil then
    local tmptable = {};
    for i = 1,#u.hostedUnits.Aircraft do tmptable[i]=u.hostedUnits.Aircraft[i]; end -- made clean copy of just the values we need
    for k,v in ipairs(tmptable) do --operate on the copy you could not use idx or pairs here it doesn't matter just a sample.
      pcall(ScenEdit_DeleteUnit,{guid=v});
      print('deleted unit ' .. k .. ' with guid ' .. v .. ' that was hosted on ' .. u.name)
    end
  end
end

DeleteAllHostedUnits2(ScenEdit_GetUnit({name='SUA1', guid='4FH7PU-0HMHHATCAVSIG'}));
Using a deep-copy for more involved tables\objects, ie values that are tables themselves. (duplicate by-value not reference whole structure)

Code: Select all

local gKH={}  --added just so I didn't have to change anything for this posted snippet.
gKH.base = {}; --added just so I didn't have to change anything for this posted snippet.

---Function to make a true copy an existing table into a real new table that has no references to old table.
-- Rarely needed, but when you really do you do.
-- Used by RandomUnitGenerator during recursion to avoid some refference problems.
-- original code from:
-- https://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value
---@param o table @table to copy
---@param seen? table @table used during recursion
---@return table @an unreferenced copy of the table fed to it.
function gKH.base.deepcopy(o, seen)
  seen = seen or {}
  if o == nil then return nil end
  if seen[o] then return seen[o] end

  local no
  if type(o) == 'table' then
    no = {}
    seen[o] = no

    for k, v in next, o, nil do
      no[gKH.base.deepcopy(k, seen)] = gKH.base.deepcopy(v, seen)
    end
    setmetatable(no, gKH.base.deepcopy(getmetatable(o), seen))
  else -- number, string, boolean, etc
    no = o
  end
  return no
end

--usage
function DeleteAllHostedUnits3(u)
  if ((u ~= nil) and u.hostedUnits ~=nil) and u.hostedUnits.Aircraft ~=nil then
    local tmptable = gKH.base.deepcopy(u.hostedUnits.Aircraft);  --byvalue copy of entire table.
    for k,v in ipairs(tmptable) do  -- could use idx or pairs method this is just a quick sample.
      pcall(ScenEdit_DeleteUnit,{guid=v});
      print('deleted unit ' .. k .. ' with guid ' .. v .. ' that was hosted on ' .. u.name)
    end
  end
end
DeleteAllHostedUnits3(ScenEdit_GetUnit({name='SUA1', guid='4FH7PU-0HMHHATCAVSIG'}));