only problem is that prestate is going to be the key name during the loop (its what were returning - k), its not (necesarily) going to be an index/number that you can increment. the original way works fine.
Ah yes, you are correct, so storing t.i is still necessary.
question: in the iter function do i have to do a table.wipe( t ) or can i just get away with t.data = nil? the rest are just numbers and strings so should de-reference and get gc'd fairly quickly only the source table would hang around for ages and stop this table from being gc'd.
table.wipe(t) is needed if you are using a table pool that is used across your entire addon. t.data = nil is fine if you are using a local table pool that only your iterator will use.
local function pairsByKey (t, f)
local a = {}
for n in pairs(t) do table.insert(a, n) end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if a[i] == nil then return nil
else return a[i], t[a[i]]
end
end
return iter
end
...
function foo()
local bar = {
["key1"]="value1",
["key2"]="value2",
["key3"]="value3",
}
for k, v in pairsByKey(bar) do
dostuffwith(k, v)
end
end
I have no idea if what I do leads to a lot of garbage or not, it does a nice job of hiding the complexity in the iterator so that the code itself looks simple...It has the added advantage of allowing for a sorter to be passed as a second argument.
Similarly I use the same principle to iterate over a raid/party :
local function RaidIterator()
local i = 0
return function ()
i = i + 1
local unit
if UnitInRaid("player") and i<=GetNumRaidMembers() then
return "raid"..i
else
numPartyMembers = GetNumPartyMembers()
if i<= numPartyMembers then
return "party"..i
elseif i==numPartyMembers+1 then
return "player"
end
end
end
end
local function pairsByKey (t, f)
local a = {}
for n in pairs(t) do table.insert(a, n) end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if a[i] == nil then return nil
else return a[i], t[a[i]]
end
end
return iter
end
...
function foo()
local bar = {
["key1"]="value1",
["key2"]="value2",
["key3"]="value3",
}
for k, v in pairsByKey(bar) do
dostuffwith(k, v)
end
end
I have no idea if what I do leads to a lot of garbage or not
It creates a table and a function everytime it's called, depending on how you use it, it could produce some quite a bit of garbage. Something like this will only create the required iteration function and sorted array when required.
[SIZE=2]
[SIZE=2]local pairsByKeys; do[/SIZE]
[SIZE=2]local sortedtbl, iter[/SIZE]
[SIZE=2]function pairsByKeys(tbl, f)[/SIZE]
[SIZE=2] if sortedtbl then[/SIZE]
[SIZE=2] wipe(sortedtbl)[/SIZE]
[SIZE=2] else[/SIZE]
[SIZE=2] sortedtbl = {}[/SIZE]
[SIZE=2] function iter(tbl, i)[/SIZE]
[SIZE=2] i = i + 1[/SIZE]
[SIZE=2] if sortedtbl[i] == nil then[/SIZE]
[SIZE=2] return nil, nil, nil[/SIZE]
[SIZE=2] else[/SIZE]
[SIZE=2] return i, sortedtbl[i], tbl[sortedtbl[i]][/SIZE]
[SIZE=2] end[/SIZE]
[SIZE=2] end[/SIZE]
[SIZE=2] end[/SIZE]
[SIZE=2] for k, v in pairs(tbl) do[/SIZE]
[SIZE=2] table.insert(sortedtbl, k)[/SIZE]
[SIZE=2] end[/SIZE]
[SIZE=2] table.sort(sortedtbl, f)[/SIZE]
[SIZE=2] return iter, tbl, 0[/SIZE]
[SIZE=2]end[/SIZE]
[SIZE=2]end[/SIZE]
[SIZE=2]local tbl = {[/SIZE]
[SIZE=2]key1 = "val1",[/SIZE]
[SIZE=2]key2 = "val2",[/SIZE]
[SIZE=2]key3 = "val3",[/SIZE]
[SIZE=2]key4 = "val4",[/SIZE]
[SIZE=2]key5 = "val5",[/SIZE]
[SIZE=2]key6 = "val6",[/SIZE]
[SIZE=2]}[/SIZE]
[SIZE=2]for i, k, v in pairsByKeys(tbl) do[/SIZE]
[SIZE=2]print(k, v)[/SIZE]
[SIZE=2]end[/SIZE]
[/SIZE]
Slakah, your code is using the same "sortedtbl" for all iterators, so you can't include two loops at the same time.
I think Xinhuan's code is the best but changed to the following to have the prestate "stored" outside the table :
local spairs
do
local function iter(state, index)
index = index + 1
local key = state[index]
if key ~= nil then
return index, key, state.t[key]
end
end
function spairs(t, sortFunc)
local state = {}
local index = 1
for k in pairs(t) do
state[index] = k
index = index + 1
end
table.sort(state, sortFunc)
state.t = t
return iter, state, 0
end
end
-- Sample usage:
for index, key, value in spairs(t) do
-- Do something with key, value
end
Honestly, there oare few cases when you want to iterate through sorted keys so you could do it "manually" instead of using such an iterator.
Ah yes, you are correct, so storing t.i is still necessary.
table.wipe(t) is needed if you are using a table pool that is used across your entire addon. t.data = nil is fine if you are using a local table pool that only your iterator will use.
I have no idea if what I do leads to a lot of garbage or not, it does a nice job of hiding the complexity in the iterator so that the code itself looks simple...It has the added advantage of allowing for a sorter to be passed as a second argument.
Similarly I use the same principle to iterate over a raid/party :
It creates a table and a function everytime it's called, depending on how you use it, it could produce some quite a bit of garbage. Something like this will only create the required iteration function and sorted array when required.
I think Xinhuan's code is the best but changed to the following to have the prestate "stored" outside the table :
Honestly, there oare few cases when you want to iterate through sorted keys so you could do it "manually" instead of using such an iterator.