View Single Post
07-12-12, 08:32 PM   #2
kaels
A Cyclonian
AddOn Author - Click to view addons
Join Date: Jan 2011
Posts: 46
Your iterator is getting mucked up because you're using pairs over a table whose indexes are changing inside the loop. As a rule, if you're iterating over a table with pairs or ipairs, you should try to avoid using table.remove and table.insert on that table inside the loop to avoid the sort of unpredictable behaviour you're seeing here.

Try this - it's definitely not optimized, but it should work:

lua Code:
  1. local function tableMerge(...)
  2.     if select('#', ...) < 3 then error("tableMerge", "inputs") return end
  3.     local toReturn = {}
  4.     for i=1, select('#', ...) do
  5.         local tab = select(i, ...)
  6.         if type(tab) == "table" then --ignore any inputs which aren't tables
  7.             if not toReturn[1] then
  8.                 for j,spellbar in pairs(tab) do -- do it this way so we don't actually do anything to the tables passed in
  9.                     table.insert(toReturn, spellbar)
  10.                     print("Adding " .. spellbar)
  11.                 end
  12.             else
  13.                 local n = #toReturn -- get the size of the array
  14.                 -- iterate over the indices in the array
  15.                 for j = 1,n do -- for everything that's common up to this point in iteration:
  16.                     local v = toReturn[j] -- get the value at this index
  17.                     print("Testing " .. v)
  18.                     local found = false
  19.                     for k,spellbar in pairs(tab) do -- for everything in this new table to check
  20.                         if v == spellbar then       -- if we found that the spellbar exists in this new table, say we found it
  21.                             found = true
  22.                             print("Found " .. spellbar)
  23.                         end
  24.                     end
  25.                     if not found then -- if we didn't find this element, then remove it from the common table
  26.                         print("Removing " .. toReturn[j])
  27.                         toReturn[j] = nil -- set to nil to remove without changing the array
  28.                     end
  29.                 end
  30.                 -- now we need to clean up the table
  31.                 for j = 1,n do -- iterate over the array again
  32.                     local v = toReturn[j] -- get the value at this index
  33.                     if v then -- look for non-empty values
  34.                         toReturn[j] = nil -- clear the current index
  35.                         table.insert(toReturn, v) -- reinsert the value at the first non-empty index
  36.                     end
  37.                 end            
  38.             end        
  39.         end
  40.     end
  41.     return toReturn -- return everything that's common
  42. end

And here's an alternative that may be quicker (only runs a cleanup function once) but won't preserve order, and might actually be slower (at least for a small number of tables) because it has to use pairs:
lua Code:
  1. local function tableMerge(...)
  2.     if select('#', ...) < 3 then error("tableMerge", "inputs") return end
  3.     local toReturn = {}
  4.     local n -- define n outside the loop so we can use it later
  5.     for i=1, select('#', ...) do
  6.         local tab = select(i, ...)
  7.         if type(tab) == "table" then --ignore any inputs which aren't tables
  8.             if not toReturn[1] then
  9.                 for j,spellbar in pairs(tab) do -- do it this way so we don't actually do anything to the tables passed in
  10.                     table.insert(toReturn, spellbar)
  11.                     print("Adding " .. spellbar)
  12.                 end
  13.                 n = #toReturn -- get the size of the initial table - it won't get any bigger than this
  14.             else
  15.                 -- we have to iterate with pairs because after the first run, our table will have empty indices
  16.                 for j, v in pairs(toReturn) do -- for everything that's common up to this point in iteration:
  17.                     print("Testing " .. v)
  18.                     local found = false
  19.                     for k,spellbar in pairs(tab) do -- for everything in this new table to check
  20.                         if v == spellbar then       -- if we found that the spellbar exists in this new table, say we found it
  21.                             found = true
  22.                             print("Found " .. spellbar)
  23.                         end
  24.                     end
  25.                     if not found then -- if we didn't find this element, then remove it from the common table
  26.                         print("Removing " .. toReturn[j])
  27.                         toReturn[j] = nil -- set to nil to remove without changing the array
  28.                     end
  29.                 end
  30.                 -- note that the resulting table has a bunch of empty indices after each loop          
  31.             end        
  32.         end
  33.     end
  34.     -- now we can do a single cleanup run
  35.     for j = 1, n do -- iterate over the maximum size of the table
  36.         local v = toReturn[j] -- get the value at the current index
  37.         if v then -- if we have a non-nil value
  38.             toReturn[j] = nil -- remove the value from its current index
  39.             table.insert(toReturn, v) -- put the value in the first empty index
  40.         end
  41.     end
  42.     return toReturn -- return everything that's common
  43. end
You'll want to check for typos, I haven't tested either code.

Last edited by kaels : 07-12-12 at 08:52 PM. Reason: modified second suggestion to behave better
  Reply With Quote