test = {
["key1"]=1,
["key2"]=1,
["key3"]=0,
}
local sortedTest = {}
for k in pairs(test) do
sortedTest[ #sortedTest +1 ] = k
end
table.sort(sortedTest)
-----
for i = 1 #sortedTest do
print(sortedTest[i], test[ sortedTest[i] ] )
end
by that, he means that only "arrays" have a defined order (and so can be re-ordered via a sort). in lua, arrays are tables with consecutive integer indices, whereas dictionaries are pretty much all other tables.
Thanks guys, great workaround, it surely do fit right in, even with the additional array for the sort. Also, I didn't know about that #arrayname stuff, looks it gets the number of elements, cute ;)
You'd better with one table with all keys and another table with all values. That's only 2 tables even with 700 entries in them. Your solution means 701 tables for 700 entries. This is pretty bad if this has to be often and dynamically updated during the addon lifetime.
Thanks guys, great workaround, it surely do fit right in, even with the additional array for the sort. Also, I didn't know about that #arrayname stuff, looks it gets the number of elements, cute ;)
#table only returns the number of elements in a sequenced index. So if you have something at 1 - 5 & 35, #table is going to return 5 not 6.
[FONT=Verdana]i'm not sure how "bad" this is code wise (probably very), its from a couple of different places i found while learning lua. [/FONT] [FONT=Verdana]
function ArkInventory.spairs( tbl, comparator )
if type( tbl ) ~= "table" then
return
end
local sortedKeys = {}
for k in pairs( tbl ) do
tinsert( sortedKeys, k )
end
sort( sortedKeys, comparator )
local i = 0
local function _f( _s, _v )
i = i + 1
local k = sortedKeys[i]
if k ~= nil then
return k, tbl[k]
end
end
return _f, nil, nil
end
and then called;[/FONT] [FONT=Verdana] [/FONT] [FONT=Verdana]
for pn, pd in ArkInventory.spairs( ArkInventory.db.realm.player.data ) do
or supply a comparitor [/FONT] [FONT=Verdana]
for rid in ArkInventory.spairs( r, function(a,b) return ( r[a].order or 0 ) < ( r[b].order or 0 ) end ) do
it works fine in my mod, but as i said, not sure how "badly" coded it is. [/FONT]
You'd better with one table with all keys and another table with all values. That's only 2 tables even with 700 entries in them. Your solution means 701 tables for 700 entries. This is pretty bad if this has to be often and dynamically updated during the addon lifetime.
yeah, that was more for illustration purposes. also, not sure how you sort two tables at the same time using one table as the keys and the other as the values.
local myTable = { ["foo"] = 1, ["bar"] = 2, ["over"] = 9000 }
local sortedKeys = { }
for k, v in pairs(myTable) do table.insert(sortedKeys, k) end
local function sortMyTable(a, b)
return myTable[a] > myTable[b]
end
table.sort(sortedKeys, sortMyTable)
==> { "foo", "bar", "over" }
for i, k in ipairs(sortedKeys) do
print(myTable[k])
end
==> 1, 2, 9000
function ArkInventory.spairs( tbl, comparator )
if type( tbl ) ~= "table" then
return
end
local sortedKeys = {}
for k in pairs( tbl ) do
tinsert( sortedKeys, k )
end
sort( sortedKeys, comparator )
local i = 0
local function _f( _s, _v )
i = i + 1
local k = sortedKeys[i]
if k ~= nil then
return k, tbl[k]
end
end
return _f, nil, nil
end
That's pretty bad, as you're creating a new table and a new function every time you run your spairs function...
yeah, that was more for illustration purposes. also, not sure how you sort two tables at the same time using one table as the keys and the other as the values.
You do this by writing your own sort function (when swapping 2 entries, you perform the swap in both tables). Here, I mean writing the actual sort function, not the comparison function. Shell sort is almost as efficient as quick sort in Lua.
yeah, but if you're explaining why sort doesn't work on a table with "key1" and "key2" as indices, i'm thinking maybe talking about writing a sort function is not helpful.
the 2ndary sortable table (as phanx posted) is probably the better move for a beginner. you get the "random access" of a dictionary with the sortability of an array. you just need to an extra layer of indirection.
Aww, my poor topic became sorta huge :D
I did it very close to the way Phanx suggested and it works wonders. It is also worth noting that # does not seems to work on dictionary stuff too, poor dictionaries are left in dark it seems.
#table only returns the number of elements in a sequenced index. So if you have something at 1 - 5 & 35, #table is going to return 5 not 6.
Not true, the behavior of #table is considered 'undefined' in the case that the table is anything other than a continuous array of integers without any gaps. It could return 35 in that case or it could return 5.
This is why ipairs() should always be used over 'for i = 1, #table' in iterators if you are expecting the same behavior as ipairs but can not fully trust the source 100% because you did not create the table yourself.
yeah, that was more for illustration purposes. also, not sure how you sort two tables at the same time using one table as the keys and the other as the values.
You do this by writing your own sort function (when swapping 2 entries, you perform the swap in both tables). Here, I mean writing the actual sort function, not the comparison function. Shell sort is almost as efficient as quick sort in Lua.
Another more simple option is to just use the original hash table and an additional array that uses the keys from the hash table as its value to act as a sorted reference to the hash table. You use the array first to get the sorted keys but when you need the values you just look them up from the has table. You do have to iterate over the entire hash table though when you want to delete an entry and you can't have more than one entry with the same key like you could using two arrays.
That's pretty bad, as you're creating a new table and a new function every time you run your spairs function...
i thought iterator functions were different? dont they return a function that generates a pointer to the next element, or nil if reached the end?
if you look at the code it cant be getting created more than once or the variable i would never be anything except 1 and it'd loop infinitely. (unless i read it wrong, which is possible)
i had presumed that by returning the inside local iterator function it just kept calling that local function at every cycle in the for loop - so both functions (and the table) should only get created once?
That's pretty bad, as you're creating a new table and a new function every time you run your spairs function...
Actually Phanx, the way Arkayenro wrote it, his spairs() function is re-entrant and you can run a spairs() loop inside a spairs() loop. If he was to upvalue the sortedKeys[] table, and also the upvalues i and the function, the function wouldn't be reentrant. In particular, all keys already iterated over should be capable of being safely deleted in an iterator loop, so care must be taken in the calling functions not to delete keys in the inner loop that the outer loop has not iterated over.
The solution then would be to create a recycled table pool to draw from as well as a new closure created for a new upvalue i per reentrant call. The upvalue/closure isn't strictly necessary because the value and any others can just be stored in sortedKeys[] table itself (such as in sortedKeys.i), and sortedKeys[] be returned as the 2nd return (the table to return) to the iterator call to be passed to the 1st return (the iterator function). GatherMate uses this method.
Either way, a new table is required per spairs() call, unless the code that uses spairs() is confirmed never to call spairs() within spairs().
i had presumed that by returning the inside local iterator function it just kept calling that local function at every cycle in the for loop - so both functions (and the table) should only get created once?
Yes, this is correct. Phanx was more concerned about the garbage generated should spairs() get called extremely often.
The function can be rewritten this way to avoid the new iterator function per spairs() call:
local function iter(t, prestate)
t.i = t.i + 1
local k = t[t.i]
if k ~= nil then
return k, t.data[k]
end
-- You can return wipe(t) and return t to the table pool here if you used one
-- Just take care you don't wipe t.data recursively.
end
function ArkInventory.spairs( tbl, comparator )
if type( tbl ) ~= "table" then
return
end
-- Create a new table with the keys from tbl[] as values in sortedKeys[]
local sortedKeys = {} -- You can get a table from a table pool here
local i = 1
for k in pairs( tbl ) do
sortedKeys[i] = k -- tinsert() is hella slow, avoid it
i = i + 1
end
-- Sort the keys
sort( sortedKeys, comparator )
-- Initialize iterator state information
sortedKeys.i = 0
sortedKeys.data = tbl
return iter, sortedKeys, nil
end
Edit: This part below doesn't work, explained 1 and 2 posts down.
Note that the "prestate" variable isn't actually used in iter(), although you can use it to avoid using sortedKeys.i, since in the function, prestate will be equal to sortedKeys.i at the start of the function. To do this, spairs() is modified to "return iter, sortedKeys, 0" and iter() is modified to
local function iter(t, prestate)
prestate = prestate + 1
local k = t[prestate]
if k ~= nil then
return k, t.data[k]
end
end
Note that the "prestate" variable isn't actually used in iter(), although you can use it to avoid using sortedKeys.i, since in the function, prestate will be equal to sortedKeys.i at the start of the function. To do this, spairs() is modified to "return iter, sortedKeys, 0" and iter() is modified to
local function iter(t, prestate)
prestate = prestate + 1
local k = t[prestate]
if k ~= nil then
return k, t.data[k]
end
end
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.
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.
If I just do
Then I just get them in random order:
I'm trying to get stuff to show in either the order I defined in the first place, or as last resort - alphabetical, but both fails. Few other tries:
prints nothing at all...
does not alter the places...
gives no message from the sort function at all...
I'm obviously missing something basic here... this should work :(
http://www.lua.org/pil/19.3.html
you'd need to do:
table[1] = { name="key1", value=1 }
table[2] = { name="key2", value=1 }
table[3] = { name="key3", value=0 }
or some such...
You'd better with one table with all keys and another table with all values. That's only 2 tables even with 700 entries in them. Your solution means 701 tables for 700 entries. This is pretty bad if this has to be often and dynamically updated during the addon lifetime.
#table only returns the number of elements in a sequenced index. So if you have something at 1 - 5 & 35, #table is going to return 5 not 6.
ie:
[/FONT] [FONT=Verdana][/FONT] [FONT=Verdana] and then called;
[/FONT] [FONT=Verdana]
[/FONT] [FONT=Verdana] or supply a comparitor
[/FONT] it works fine in my mod, but as i said, not sure how "badly" coded it is.
yeah, that was more for illustration purposes. also, not sure how you sort two tables at the same time using one table as the keys and the other as the values.
That's pretty bad, as you're creating a new table and a new function every time you run your spairs function...
You do this by writing your own sort function (when swapping 2 entries, you perform the swap in both tables). Here, I mean writing the actual sort function, not the comparison function. Shell sort is almost as efficient as quick sort in Lua.
the 2ndary sortable table (as phanx posted) is probably the better move for a beginner. you get the "random access" of a dictionary with the sortability of an array. you just need to an extra layer of indirection.
I did it very close to the way Phanx suggested and it works wonders. It is also worth noting that # does not seems to work on dictionary stuff too, poor dictionaries are left in dark it seems.
Not true, the behavior of #table is considered 'undefined' in the case that the table is anything other than a continuous array of integers without any gaps. It could return 35 in that case or it could return 5.
This is why ipairs() should always be used over 'for i = 1, #table' in iterators if you are expecting the same behavior as ipairs but can not fully trust the source 100% because you did not create the table yourself.
Another more simple option is to just use the original hash table and an additional array that uses the keys from the hash table as its value to act as a sorted reference to the hash table. You use the array first to get the sorted keys but when you need the values you just look them up from the has table. You do have to iterate over the entire hash table though when you want to delete an entry and you can't have more than one entry with the same key like you could using two arrays.
i thought iterator functions were different? dont they return a function that generates a pointer to the next element, or nil if reached the end?
if you look at the code it cant be getting created more than once or the variable i would never be anything except 1 and it'd loop infinitely. (unless i read it wrong, which is possible)
i had presumed that by returning the inside local iterator function it just kept calling that local function at every cycle in the for loop - so both functions (and the table) should only get created once?
Actually Phanx, the way Arkayenro wrote it, his spairs() function is re-entrant and you can run a spairs() loop inside a spairs() loop. If he was to upvalue the sortedKeys[] table, and also the upvalues i and the function, the function wouldn't be reentrant. In particular, all keys already iterated over should be capable of being safely deleted in an iterator loop, so care must be taken in the calling functions not to delete keys in the inner loop that the outer loop has not iterated over.
The solution then would be to create a recycled table pool to draw from as well as a new closure created for a new upvalue i per reentrant call. The upvalue/closure isn't strictly necessary because the value and any others can just be stored in sortedKeys[] table itself (such as in sortedKeys.i), and sortedKeys[] be returned as the 2nd return (the table to return) to the iterator call to be passed to the 1st return (the iterator function). GatherMate uses this method.
Either way, a new table is required per spairs() call, unless the code that uses spairs() is confirmed never to call spairs() within spairs().
Yes, this is correct. Phanx was more concerned about the garbage generated should spairs() get called extremely often.
The function can be rewritten this way to avoid the new iterator function per spairs() call:
Edit: This part below doesn't work, explained 1 and 2 posts down.
Note that the "prestate" variable isn't actually used in iter(), although you can use it to avoid using sortedKeys.i, since in the function, prestate will be equal to sortedKeys.i at the start of the function. To do this, spairs() is modified to "return iter, sortedKeys, 0" and iter() is modified to
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.
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.