[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Lua
. . -> [Subject]  Sorting tables by heading name

Sorting tables by heading name

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


Pages: 1 2  

Posted by Tiopon   USA  (71 posts)  [Biography] bio
Date Sat 24 Apr 2010 09:11 PM (UTC)

Amended on Sat 24 Apr 2010 09:18 PM (UTC) by Tiopon

Message
So my current problem is that my huge table is getting very difficult to see things in. I've tried to sort it using information from the following posts:
http://www.mushclient.com/forum/bbshowpost.php?id=6036
http://www.mushclient.com/forum/bbshowpost.php?id=8491
http://www.mushclient.com/forum/bbshowpost.php?id=8608
http://www.mushclient.com/forum/bbshowpost.php?id=5643

The table itself, printed with tprint, looks sort of like this:
Quote:
1:
"name"="Relay"
"finish1"="Handoff"
"attack1"="Point"
"level"=1
2:
"name"="Busboy"
"finish1"="Ringup"
"attack1"="Clean"
"level"=1
As you can see, I'm using numbers as the main identifier for the bits (table[1], table[2], etc). I want it to sort by the actual names inside the table though, so I want it to look like:
Quote:
1:
"attack1"="Point"
"finish1"="Handoff"
"level"=1
"name"="Relay"
2:
"attack1"="Clean"
"finish1"="Ringup"
"level"=1
"name"="Busboy"
So I just want it alphabetically sorted on each inner entry... I tried doing table.sort(table[1]) and that didn't change anything... table.sort(table[1], alphanum(table[1])) also runs, but doesn't seem to change anything. I know there's some sort of thing involving making a new table to sort it, but when I ran this:
Quote:
/table2 = {} table.foreach (table, function (k) table.insert (table2, k) end) table.sort(table2) tprint(table2)

all that did for me was the following...
Quote:
1=1
2=2
which isn't especially useful. :) It appears that's the way it's supposed to be working, but what I want it to do is to grab the attack1/finish1/etc, and update the original table... if that means I need to make a temporary table that's been sorted, do table[1] = temptable, temptable = {} between each, that works... I just want it to work and be readable as opposed to the painful mess it's becoming with 269 lines in it currently, sorted only by whatever internal logic determines initial placement.
[Go to top] top

Posted by Tiopon   USA  (71 posts)  [Biography] bio
Date Reply #1 on Sat 24 Apr 2010 09:15 PM (UTC)

Amended on Sat 24 Apr 2010 09:39 PM (UTC) by Tiopon

Message
I can see that if I do the following instead:
Quote:
/table2 = {} table.foreach (table[1], function (k) table.insert (table2, k) end) table.sort(table2) tprint(table2)
I get a sorted set of the headings like this:
Quote:
1="attack1"
2="finish1"
3="level"
4="name"
Next question... how do I get that back into the main table, and iterate the sorting over the table[1] through table[45] in a less painful way... I'm guessing it'll be a table.foreach done on the table itself... but... I'm so confused on how to get it back. I did search the forum and didn't find anything that seemed to be regarding this directly before I posted... :(

Edit: Trying this does work, but doesn't do anything because the numbers are already sorted. Just displays the whole list again with 'key' instead of the number, and takes out the indenting.
Quote:
/table2 = {} table.foreach (table, function (k) table.insert (table2, k) end) table.sort(table2) for k,v in ipairs (table2) do print ("key =",v) table.foreach(table[v], print) end
but if I try to sort one of the actual fields, like this:
Quote:
/table2 = {} table.foreach (table[1], function (k) table.insert (table2, k) end) table.sort(table2) for k,v in ipairs (table2) do print ("key =",v) table.foreach(table[1][v], print) end
I get the following error:
Quote:
[string "Command line"]:1: bad argument #1 to 'foreach' (table expected, got string)
If I understand properly, this means that it's not actually able to do the sorting itself again, right? Because if I do have it doing the lookup properly, I could have it saving the values to a temporary third table, then overwrite the first table segment with the third table... or is there a better way to do this? *sighs*
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #2 on Sat 24 Apr 2010 09:55 PM (UTC)
Message
This should do everything you need.

table.sort(tbl, function(t1, t2)
  return t1.name < t2.name
end)


I'm sure you'd have gotten it with a bit more research and testing though. :)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Tiopon   USA  (71 posts)  [Biography] bio
Date Reply #3 on Sat 24 Apr 2010 10:10 PM (UTC)
Message
So how exactly how am I supposed to use that... like this?
Quote:
table.sort(table[1], function(t1,t2) return t1.name < t2.name end)
or
Quote:
table.sort(table, function(t1,t2) return t1.name < t2.name end)

Am I supposed to do the table2 thing first? Sorry, it's just not seeming to do anything... I'm just not properly comprehending how the process is working.
[Go to top] top

Posted by Nick Gammon   Australia  (22,989 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Sat 24 Apr 2010 10:37 PM (UTC)

Amended on Sat 24 Apr 2010 10:38 PM (UTC) by Nick Gammon

Message
The module pairsbykeys which ships with MUSHclient will do that. Given a table, it iterates through it in the order of the keys (so attack1 would come first):


require "pairsbykeys"

-- This prints the math functions in key order
for k, v in pairsByKeys (math) do
  print (k, v)
end -- for


(Internally it copies the keys into a temporary table, sorts that, and then returns the items one by one).

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #5 on Sat 24 Apr 2010 11:04 PM (UTC)

Amended on Sat 24 Apr 2010 11:05 PM (UTC) by Twisol

Message
Tiopon said:

So how exactly how am I supposed to use that... like this?
Quote:
table.sort(table[1], function(t1,t2) return t1.name < t2.name end)
or
Quote:
table.sort(table, function(t1,t2) return t1.name < t2.name end)

Am I supposed to do the table2 thing first? Sorry, it's just not seeming to do anything... I'm just not properly comprehending how the process is working.


The second one. That function you pass to table.sort is called a comparator, and it's called to compare two values within the table, to decide which comes first. In your table, each value is a table itself, and when two of them are compared, they're passed to the comparator. In the comparator, I determine the order based on each one's name,


@Nick: I may have misunderstood Tiopon, but I believe he wants to sort by value, not key.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Tiopon   USA  (71 posts)  [Biography] bio
Date Reply #6 on Sat 24 Apr 2010 11:24 PM (UTC)

Amended on Sat 24 Apr 2010 11:30 PM (UTC) by Tiopon

Message
Heh. Just realized now what Twisol's suggestion did... resorted my table by name and losing its key sorting, which would be fine if the numbers weren't directly related to the table. :p Time to fix 268 rows in Excel... Done.

Ah, thought that pairsByKeys might be the right one... So here's what I did to actually have it display an individual entry:
Quote:
/for k,v in pairsByKeys(table[1]) do print (k,v) end


What I'm not quite sure about is how to get that to update the original table properly and not just display it beautifully... I suppose I can just leave it messy in reality and show it nicely when done, but what do I have to do to make that work... I was trying to come up with a for ipairs that would let me do all the tables, but failing. Now that my table is fixed up again and I have an Excel backup for if I screw it up more with testing, let's see what we can do... heh.

Edit: Twisol, your suggestion would have worked great if I wanted was to change
Quote:
1:
"attack1"="Point"
"finish1"="Handoff"
"level"=1
"name"="Relay"
2:
"attack1"="Clean"
"finish1"="Ringup"
"level"=1
"name"="Busboy"
into
Quote:
1:
"attack1"="Clean"
"finish1"="Ringup"
"level"=1
"name"="Busboy"
2:
"attack1"="Point"
"finish1"="Handoff"
"level"=1
"name"="Relay"
but I was trying to sort the attack1/finish/level/name, not based on the "name" field. :)

So this will display the whole listing... there's 45 subtables under the main one, but if I can make it not be hardcoded that would be nice... be nicer if I can have it update the table itself. :)
Quote:
/for i=1,45 do for k,v in pairsByKeys(table) do print (k,v) end end
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #7 on Sat 24 Apr 2010 11:26 PM (UTC)

Amended on Sat 24 Apr 2010 11:29 PM (UTC) by Twisol

Message
I see, I did misunderstand. No, you cannot sort the non-numeric keys in a table. Lua tables are basically hashmaps or associative arrays. Pairs are inherently unsorted, and the only way to actually sort by anything else is exactly what you found: turning a=b into 1={a,b}.

There is generally no reason to sort by key unless you're going to use that order immediately, in which case Nick's solution would work excellently.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (22,989 posts)  [Biography] bio   Forum Administrator
Date Reply #8 on Sat 24 Apr 2010 11:28 PM (UTC)
Message
You can't "update the original table properly" - if you use named keys for tables in Lua they are stored by hash and therefore retrieved in what seems a random fashion.

Only numeric keys (1, 2, 3 etc) are retrieved in sequential order, and only then if you use ipairs rather than pairs.

The only thing you can do is sort them upon retrieval (which is very fast).

It's not really "messy" - the way the data is stored internally isn't a big concern, the important thing is to present it nicely when required.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Tiopon   USA  (71 posts)  [Biography] bio
Date Reply #9 on Sat 24 Apr 2010 11:31 PM (UTC)
Message
Is there a good way to save it into a new table using the pairsByKeys function? If I can save it into a new table, I should be able to use that temporary table to replace the new table when it's done... just parse through the tables twice, once to sort, once to replace. I suppose a third time to wipe the temporary tables.
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #10 on Sat 24 Apr 2010 11:37 PM (UTC)

Amended on Sat 24 Apr 2010 11:38 PM (UTC) by Twisol

Message
But see, what you are asking is inherently impossible. You can't reorder non-numeric keys. The order you get pairs from pairs() has no.discernable order, and indeed isn't supposed to have an order. It simply returns the pairs in whatever order it finds them in.

ipairs() only gets around this by literally using a loop starting at 1 and going up to the first nonexistent entry.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Tiopon   USA  (71 posts)  [Biography] bio
Date Reply #11 on Sat 24 Apr 2010 11:44 PM (UTC)

Amended on Sat 24 Apr 2010 11:47 PM (UTC) by Tiopon

Message
But if I can display it correctly, I should be able to, instead of making it display, save it to a new table, right? Should be able to do something like (but not exactly):
Quote:
/table2 = {} for j=1,45 do table2[j] = {} for k,v in pairsByKeys(table[j]) do table2[j][k]=v end end


If I have it create a second table, I can then save the second table instead of the first, and next time it loads up the table on a restart I'll have a nicely sorted table (until the next time it updates). Also, I'll be able to use tprint on the second table to have everything nicely indented. :)

Edit: So I suppose using i as my value wasn't the best choice, since it gets grabbed by the code as 'italicize'. Changing the i to j, so it doesn't get snagged.

Or is what you're saying that the code will save in random order based on its current hash, so regardless of any prior sorting, it's not going to be sorted when it's saved? :) Just realized now that might be what you were saying.
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #12 on Sat 24 Apr 2010 11:48 PM (UTC)

Amended on Sat 24 Apr 2010 11:50 PM (UTC) by Twisol

Message
Incorrect. Every insertion in inherently unsorted as well. This is why this code:

tbl = {}
tbl.a = 1
tbl.b = 2
tbl.c = 3
tprint(tbl)


results in (as just one possibility):

"b": 2
"a": 1
"c": 3


In response to your edit: Yes, that might be it. The order tprint comes up with for a table will always be the same no matter how many times you run it, but once you modify it, that all goes out the window.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Tiopon   USA  (71 posts)  [Biography] bio
Date Reply #13 on Sat 24 Apr 2010 11:53 PM (UTC)

Amended on Sun 25 Apr 2010 12:06 AM (UTC) by Tiopon

Message
So what I really need is an alias similar to the following...
Quote:
/for j=1,45 do print(j .. ":") for k,v in pairsByKeys(table[j]) do print(" \"" .. k .. "\"\=\"" .. v .. "\"") end end
... Which, at least as I try it now, appears the same as tprint, but is sorted alphabetically...

Easy enough. Copied the tprint function to tprinta and changed the 'value in pairs' to be 'value in pairsByKeys'... everything else the same (well, besides renaming tprint to tprinta everywhere in it so the functions don't fight) and tprinta gives me my nicely sorted lists. :) tprinta follows:
--
--  tprinta.lua

--[[

For debugging what tables have in them, prints recursively

See forum thread:  http://www.gammon.com.au/forum/?id=4903

eg.

require "tprinta"

   tprinta (GetStyleInfo (20))

--]]

require "pairsbykeys"

function tprinta (t, indent, done)
  -- in case we run it standalone
  local Note = Note or print
  local Tell = Tell or io.write
  
  -- show strings differently to distinguish them from numbers
  local function show (val)
    if type (val) == "string" then
      return '"' .. val .. '"'
    else
      return tostring (val)
    end -- if
  end -- show
  -- entry point here
  done = done or {}
  indent = indent or 0
  for key, value in pairsByKeys (t) do
    Tell (string.rep (" ", indent)) -- indent it
    if type (value) == "table" and not done [value] then
      done [value] = true
      Note (show (key), ":");
      tprinta (value, indent + 2, done)
    else
      Tell (show (key), "=")
      print (show (value))
    end
  end
end

return tprinta
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #14 on Sun 25 Apr 2010 12:04 AM (UTC)
Message
Excellent :)


Nick, I'd actually suggest using pairsByKeys in tprint by default. It's certainly very useful for inspecting tables if you can look at things in a natural order. Otherwise, a tprinta like above would be a welcome addition to tprint.lua.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] 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.


41,480 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

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

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

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

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]