Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ MUSHclient ➜ Lua ➜ Separate numerically keyed table to get an order of keyed table?

Separate numerically keyed table to get an order of keyed table?

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Rivius   (99 posts)  Bio
Date Mon 31 Oct 2011 06:46 PM (UTC)
Message
For a particular task I am trying to accomplish, I require the ability to quickly check for the existence of a variable using table[variable_name] for quick confirmation that it exists. I use this pretty frequently. Recently, however, I've learned that if I kept tabs on the order of this table, I would be able to accomplish another task. The thing is, the keys can be deleted after they have existed for a set amount of time, or if I clear them before then (I use timers for this).

The problem this creates is, if I delete some keys out of order, to delete them in the corresponding numerically keyed table, I would need to loop through it, compare the names, and table.remove.

I just want to reduce the performance cost, since I do this fairly frequently.

TO illustrate in code what I mean:

I would create a key and do:


table["dog"] = true
table.insert(reference_table, "dog")


but when I'm reading to clear it, I have to do:

table["dog"] = nil
for n,i in ipairs(reference_table) do
   if i == "dog" then
       table.remove(reference_table, n)
   end
end


Is there any way I can try to maintain a semblance of order and easily add and remove entries without having to resort to looping through the ordered table and picking off entries? I admit I know little about lua and performance, but I imagine looping through like this as often as I may need to call this will get a little costly.

Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #1 on Mon 31 Oct 2011 07:15 PM (UTC)

Amended on Mon 31 Oct 2011 07:28 PM (UTC) by Twisol

Message
The most optimal thing I can think of looks like this:


-- adding
table.insert(reference_table, "dog")
table['dog'] = #reference_table

-- removing
var idx = table["dog"]
table.remove(reference_table, idx)
for i=idx, #reference_table do
  var k = reference_table[i]
  table[k] = table[k] - 1
end
table["dog"] = nil


I'm storing the current index of each item as its key, instead of just using true. That lets me remove the item directly. The new issue here is that table.remove moves later items up by one, so those indices have now changed. I use the stored index for the removed element and go over all of the later ones - but only the later ones - and update their index in the main table.

In the end, there's still some looping, but the optimization is that I only go over the items that are known to need changing.

In your case, you're looking for the element, then shuffling all of the later ones back by one (through table.remove). So you're going over each item in the list, guaranteed, every time. (Actually it's worse than that, because you're not using 'break' as soon as you find what you were looking for.)

[EDIT]: Actually, mine goes over the latter part of the list twice when it doesn't necessarily need to. First when removing (it shuffles all items backwards), and again when updating the main list. And #reference_table goes over the whole thing to find out the length... Well, a more accurate version looks like this:

var idx = table["dog"]
while true do
  idx = idx + 1
  var k = reference_table[idx]
  
  -- end of the list
  if k == nil
    break
  end
  
  -- shuffle items backwards
  reference_table[i-1] = k
  
  -- update index in main table
  table[k] = table[k] - 1
end

reference_table[idx-1] = nil
table["dog"] = nil


Untested, but it's just merging those three bits together. Finding the end of the list, moving items back, and updating in the main table - all in one single loop.

Just goes to show, though: my original attempt actually performed worse than your original code, even with the missing break. If you added that 'break' in it really wouldn't be an awfully inefficient solution.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Rivius   (99 posts)  Bio
Date Reply #2 on Mon 31 Oct 2011 08:36 PM (UTC)
Message
Thanks, Twisol. A slight problem is that I assign these variables to things other than "true". Someone suggested I follow your solution and take a third table. Overall it feels a bit unwieldy, but there really seems to be no better way.

I'll try to see how I use your suggestion, and thanks so much again :)

Top

Posted by Nick Gammon   Australia  (23,120 posts)  Bio   Forum Administrator
Date Reply #3 on Tue 01 Nov 2011 12:31 AM (UTC)
Message
Maybe use SQLite3? You could have a database table with multiple keys (numeric key, alpha key). For speed you can make an in-memory table (not a disk one).

Failing that, a bit depends how many entries you have. Reconstructing, or searching, a medium size table wouldn't take too long.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #4 on Tue 01 Nov 2011 02:18 AM (UTC)
Message
Rivius said:
Thanks, Twisol. A slight problem is that I assign these variables to things other than "true". Someone suggested I follow your solution and take a third table. Overall it feels a bit unwieldy, but there really seems to be no better way.

I'll try to see how I use your suggestion, and thanks so much again :)


Well, there's an alternative that merges the two original tables together, if the items are always strings. So you can keep it down to two tables if you have one for ordering/existence (which is the two original tables) and one for the actual data.

I don't have much time (Halloween, hooray), but here's the idea. Instead of this:
tbl["foo"] = (index)
reference_table[(index)] = "foo"

Use:
table[(index)] = "foo"
table["foo"] = (index)

Since the items themselves are always strings, they'll never clash with the integer indices. So this easily keeps the idea of existence + ordering kept in a single table, and you can use another table for holding the actual data associated with each item.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


16,968 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.