Thread Tools Display Modes
03-30-13, 02:05 AM   #1
Clamsoda
A Frostmaul Preserver
Join Date: Nov 2011
Posts: 269
Table Iteration

Good Morning,

I was curious about how I may go about ensuring a table of mine is iterated over in a certain order (simply, top to bottom.)

I tried to use the ipairs() function for my loop, and it didn't work; and using pairs(), it reads my table in a rather erratic fashion.

My table utilizes the key and value as information, so I can't use the key as an index. The next() function seems to produce the same results as pairs().

Thanks!
  Reply With Quote
03-30-13, 03:20 AM   #2
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
The order in which key/value pairs are returned by pairs is undefined, so, basically random. next works the same way. The only way to directly walk through a table in a specific order is to use ipairs.

There are some workarounds, but they all involve creating an extra table with only the keys from the original table as indexed values, sorting it somehow, and then using ipairs on the extra table and value lookups on the original table, eg:

Code:
local t = {
    ["cow"] = "moo",
    ["pig"] = "oink",
    ["other"] = "?",
}

local order = { "cow", "pig", "other" }

for i, k in ipairs(order) do
    local v = t[k]
    -- do something with v
end
The other option would be to switch your table to an indexed table with subtables:

Code:
local t = {
    { "cow", "moo" },
    { "pig", "oink" },
    { "other", "?" },
}
Then you can sort it however you like, and use ipairs on it. However, you can't do direct value lookups anymore.

Without knowing what you're actually doing, it's hard to say what the best method will be.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
03-30-13, 07:44 AM   #3
Clamsoda
A Frostmaul Preserver
Join Date: Nov 2011
Posts: 269
Hey Phanx,

Thanks for the reply.

What I am trying to accomplish is to have two tables of relevant buffs and debuffs, and their respective modification to healing. An example would be:

Lua Code:
  1. -- ["Spell ID"] = percent
  2. local generalHealingIncreases = {
  3.     [47788] = 60,   -- Guardian Spirit
  4.     [55233] = 25    -- Vampiric Blood; needs to be adjusted for glyph.
  5. }

The way healing modifications are handled is multiplicative, from highest to lowest value. What I aim to do is sort the table from highest to lowest by hand, and by having my loop iterate over it in descending order, I can ensure the appropriate buff is calculated first.

That being said, I need to be able to modify the values through a look-up of the keys (to reflect glyphs); and the table would then need to be re-sorted to reflect the new values.

I am not sure that I like the idea of two tables. Is this a situation where metatables would be useful? I was poking around some Lua websites and there was a method utilizing metatables to emulate the next() function, but I wasn't too sure it was what I was looking for.

Thanks for your time and help (as usual ).

Last edited by Clamsoda : 03-30-13 at 09:27 AM.
  Reply With Quote
03-30-13, 11:49 AM   #4
Sharparam
A Flamescale Wyrmkin
 
Sharparam's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2011
Posts: 102
If your code is not performance critical, it may be easier to have a table of tables instead, something like:

lua Code:
  1. local buffs = { -- Unsorted buffs table
  2.     {1234, 25},
  3.     {4321, 60}
  4. }
  5.  
  6. for i, v in ipairs(buffs) do
  7.     print(i, v[1], v[2])
  8. end
  9.  
  10. --[[ Output:
  11. 1       1234    25
  12. 2       4321    60
  13. ]]
  14.  
  15. local function compare(a, b)
  16.     return a[2] > b[2]
  17. end
  18.  
  19. table.sort(buffs, compare)
  20.  
  21. for i, v in ipairs(buffs) do
  22.     print(i, v[1], v[2])
  23. end
  24.  
  25. --[[ Output:
  26. 1       4321    60
  27. 2       1234    25
  28. ]]
  29.  
  30. -- Now to find a specific buff ID and get the healing value, you could use something like:
  31.  
  32. local function getHealing(id)
  33.     for _, v in ipairs(buffs) do
  34.         if v[1] == id then return v[2] end
  35.     end
  36.     return nil -- Functions return nil by default, but whatever
  37. end
  38.  
  39. local healing = getHealing(1234) -- Returns 25
  40. healing = getHealing("this buff does not exist") -- Returns nil
  Reply With Quote
03-30-13, 11:56 AM   #5
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
Originally Posted by Clamsoda View Post
I am not sure that I like the idea of two tables. Is this a situation where metatables would be useful?
Using a metatable would still be using two tables.

Also why does the order matter if it's multiplicative?

Last edited by semlar : 03-30-13 at 12:01 PM.
  Reply With Quote
03-30-13, 12:20 PM   #6
Clamsoda
A Frostmaul Preserver
Join Date: Nov 2011
Posts: 269
Also why does the order matter if it's multiplicative?
Because the order in which you multiply, when dealing with percents, affects the out-come? Increasing an amount by 60%, then 40% yields a different answer than increasing an amount by 40%, then 60%.

I suppose you are correct about the two table approach, but the metatable could be universal for two seperate tables, yielding only 3 tables, instead of four? (Bare in mind I know nothing about metatables, I just saw it used as a solution to a problem similar to mine).

If your code is not performance critical...
I guess you could say that code is performance critical.

My approach was, and this is pretty theoretical, to sort a table of healing increases (and decreases in a separate table) by hand, and as a for-loop iterates over it (assuming it is in descending order), if a player has a buff in the table, that percentage will be applied to an amount, and the process continues. In the example posted above, if a player had BOTH Guardian Spirit and Vampiric Blood, a potential heal would be increased by 60%, then 25(or 40)%.

My approach could be horrid, but it seemed like the most simple approach, until I ran into the issue with my table being impossible to index, and impossible to iterate over with ipairs().

Thanks for the replies!

Edit:

Here was the script I was using (granted, it doesn't work in that it reads the table in any order, but it still works)
Lua Code:
  1. for k, v in pairs(generalHealingIncrease) do
  2.     if UnitAura("player", k) then
  3.         totalDamageAmount = addPercent(totalDamageAmount, v)
  4.     end
  5. end

Last edited by Clamsoda : 03-30-13 at 12:27 PM.
  Reply With Quote
03-30-13, 12:24 PM   #7
Sharparam
A Flamescale Wyrmkin
 
Sharparam's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2011
Posts: 102
Originally Posted by Clamsoda View Post
Because the order in which you multiply, when dealing with percents, affects the out-come? Increasing an amount by 60%, then 40% yields a different answer than increasing an amount by 40%, then 60%.
lua Code:
  1. local value = 100
  2. value = value * 1.6
  3. value = value * 1.4
  4. print(value) -- 224
  5. value = 100
  6. value = value * 1.4
  7. value = value * 1.6
  8. print(value) -- 224
  9.  
  10. -- Since (a * b) * c == a * b * c == a * (b * c) == a * k where k = (b * c)

Originally Posted by Clamsoda View Post
I guess you could say that code is performance critical.
Does it run very often (like in an OnUpdate script or CLEU)? If not, I think it would be pretty safe to say a few milliseconds extra won't matter too much. Or possibly you could cache the results somewhere(?) and use that in OnUpdate/CLEU.

Last edited by Sharparam : 03-30-13 at 12:28 PM.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Table Iteration


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off