Old 05-25-2019, 07:21 PM   #1
tXShooter
Human being with feelings
 
tXShooter's Avatar
 
Join Date: Aug 2017
Posts: 294
Default How do you Console tables of tables?

How should I send the following table to the console to be displayed in a grid-like manner (for troubleshooting purposes)?

Code:
SongTextBoxItems =		{
	SongTextBoxTitle = 	{"", "", "", "", "", "", "", "", "", ""},
	SongTextBoxSinger =	{"", "", "", "", "", "", "", "", "", ""},
	SongTextBoxTracks = 	{"", "", "", "", "", "", "", "", "", ""},
	SongTextBoxScene = 	{"", "", "", "", "", "", "", "", "", ""},
				}
__________________
"But be ye doers of the word, and not hearers only, deceiving your own selves."
tXShooter is offline   Reply With Quote
Old 05-25-2019, 07:42 PM   #2
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 5,938
Default

You need to loop through the entries in the parent and stringify each of the child tables. However:

- Getting a proper grid is really tricky since the console uses a proportional font.

- Because the table is keyed (rather than just indices 1,2,3,4...) the order isn't guaranteed or consistent - pairs just grabs the entries in whatever order it finds them in memory. If the order is important, it's possible to write a replacement for pairs that will iterate over them alphabetically, or there are ways to restructure the table so your exact order is kept.

Code:
local strs = {}
for key, value in pairs(SongTextBoxItems) do
  strs[#strs + 1] = key .. ":\n\t" .. table.concat(value, ", ")
end

reaper.ShowConsoleMsg(table.concat(strs, "\n"))
Lokasenna is offline   Reply With Quote
Old 05-25-2019, 07:55 PM   #3
tXShooter
Human being with feelings
 
tXShooter's Avatar
 
Join Date: Aug 2017
Posts: 294
Default

Quote:
Originally Posted by Lokasenna View Post
You need to loop through the entries in the parent and stringify each of the child tables.
Code:
local strs = {}
for key, value in pairs(SongTextBoxItems) do
  strs[#strs + 1] = key .. ":\n\t" .. table.concat(value, ", ")
end

reaper.ShowConsoleMsg(table.concat(strs, "\n"))
That worked beautifully to display it, but...

Perhaps I might need to take a step backward before worrying about displaying the contents of the table, as I seem to be having an issue understanding how to populate / use a table of tables.

My current code:
Code:
	for tab = 1, 2 do -- Song Tabs 1 & 2
		for row = 1, 5 do -- Song Rows 1 - 5
			SongTextBoxItems.SongTextBoxTitle[row*tab] = GUI.Val("Song" .. row*tab .. "Title")
			SongTextBoxItems.SongTextBoxSinger[row*tab] = GUI.Val("Song" .. row*tab .. "Singer")
			SongTextBoxItems.SongTextBoxScene[row*tab] = GUI.Val("Song" .. row*tab .. "Track")
			SongTextBoxItems.SongTextBoxTrack[row*tab] = GUI.Val("Song" .. row*tab .. "Scene")
		end
	end
I've never worked with multiple layer tables before, and my Google-foo is weak on finding suitable examples, and definitions that I can understand.

How is this done correctly?
__________________
"But be ye doers of the word, and not hearers only, deceiving your own selves."
tXShooter is offline   Reply With Quote
Old 05-25-2019, 08:52 PM   #4
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 5,938
Default

For that structure, that's pretty much what I would do.

I would personally structure it as SongTextBoxItems -> row*tab -> dataForThatSong though:

Code:
for tab = 1, 2 do -- Song Tabs 1 & 2
  for row = 1, 5 do -- Song Rows 1 - 5
    local idx = row * tab
    SongTextBoxItems[idx] = {
      Title = GUI.Val("Song" .. idx .. "Title"),
      Singer = GUI.Val("Song" .. idx .. "Singer"),
      Scene = GUI.Val("Song" .. idx .. "Track"),
      Track = GUI.Val("Song" .. idx .. "Scene"),
    }
  end
end
Any time you find yourself keeping separate sets of data where TableA[1] refers to the same thing/object/event as TableB[1] and TableC[1], etc, it's worth considering whether you'd be better off grouping the data based on that thing/object/event.
Lokasenna is offline   Reply With Quote
Old 05-26-2019, 02:25 AM   #5
X-Raym
Human being with feelings
 
X-Raym's Avatar
 
Join Date: Apr 2013
Location: France
Posts: 5,789
Default

Personally, I have recursive functions which can print a table structure not matter the level and stricture it has. One line in console per entry.

Also, to maximize performance, it first concatenate the table with this function and then print the strings (one call to ShowConsoleMsg only).
X-Raym is offline   Reply With Quote
Old 05-26-2019, 04:43 AM   #6
tXShooter
Human being with feelings
 
tXShooter's Avatar
 
Join Date: Aug 2017
Posts: 294
Default

Quote:
Originally Posted by Lokasenna View Post
For that structure, that's pretty much what I would do.

I would personally structure it as SongTextBoxItems -> row*tab -> dataForThatSong though:

Code:
for tab = 1, 2 do -- Song Tabs 1 & 2
  for row = 1, 5 do -- Song Rows 1 - 5
    local idx = row * tab
    SongTextBoxItems[idx] = {
      Title = GUI.Val("Song" .. idx .. "Title"),
      Singer = GUI.Val("Song" .. idx .. "Singer"),
      Scene = GUI.Val("Song" .. idx .. "Track"),
      Track = GUI.Val("Song" .. idx .. "Scene"),
    }
  end
end
Any time you find yourself keeping separate sets of data where TableA[1] refers to the same thing/object/event as TableB[1] and TableC[1], etc, it's worth considering whether you'd be better off grouping the data based on that thing/object/event.
I’m not sure that I follow, unless you mean to keep a table dataset as row x column as opposed to the column x row principal? In other words, keep all of one song’s data in one table, then replicate that for all of the songs per session?

My goal is to have markers and the textboxes tied together so that if either are updated, it reflects on the other, as well as the caption of the ‘start’ and ‘end’ buttons to reflect the time of the marker. These buttons can be activated manually or by scanning for an OSC signal from the mixing console depending on the value in ‘Scene’ (but about 1 out of 20 Scene changes fails to trigger a change in the script, so I have to manually insert a start or end marker, a pain when I’m the only one in the Soundbooth).

My current code (in the previous post) is failing from a nil value going to an unknown field (?), so I thought I was doing something drastically wrong, which is why I was wanting to send the tables to the console, but from the sound of your reply, I may just have an error in my concept.

I’ll keep plugging away at it.

I will say that your replies have been phenomenal and I truly appreciate your dedication to this forum.
__________________
"But be ye doers of the word, and not hearers only, deceiving your own selves."
tXShooter is offline   Reply With Quote
Old 05-26-2019, 04:45 AM   #7
tXShooter
Human being with feelings
 
tXShooter's Avatar
 
Join Date: Aug 2017
Posts: 294
Default

Quote:
Originally Posted by X-Raym View Post
Personally, I have recursive functions which can print a table structure not matter the level and stricture it has. One line in console per entry.

Also, to maximize performance, it first concatenate the table with this function and then print the strings (one call to ShowConsoleMsg only).
How do you achieve that?
__________________
"But be ye doers of the word, and not hearers only, deceiving your own selves."
tXShooter is offline   Reply With Quote
Old 05-26-2019, 07:02 AM   #8
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 5,938
Default

- An "unknown field" error means you're trying to read data from a table that isn't there. For instance:
Code:
local my_table = {
  [1] = {a: 1, b: 2, c: 3},
  [2] = {a: 4, b: 5, c: 6},
}

for i = 0, #my_table do
  reaper.ShowConsoleMsg(my_table[1].a)
end
This will fail - you can try to access my_table[0], which is nil since it doesn't exist, but then the code immediately tries access its .a property and Lua correctly freaks out because you asked it for nil.a.

Looking at this:
Code:
SongTextBoxItems.SongTextBoxTitle[row*tab] = GUI.Val("Song" .. row*tab .. "Title")
My first guess would be that you forgot to create SongTextBoxTitle before trying to access it. That is, you need to do this before looping through them:
Code:
local SongTextBoxItems = {
  SongTextBoxTitle = {},
  SongTextBoxScene = {},
  SongTextBoxSinger = {},
  SongTextBoxTrack = {},
}
- I just remembered that the GUI has something similar - new line per entry, and they get indented according to their "depth" in the table: reaper.ShowConsoleMsg(GUI.table_list(your_table)).

- As far as your table structure, you've got one table with all of the titles, one with all of the singers, etc, but that information is related - Titles[1] corresponds to Singers[1], etc - so IMO it makes sense to have a table that keeps them together. AllDataForSong[1], AllDataForSong[2], etc. It can also help with the issue of nonexistent fields, above, since you only need the top-level table to start with and the rest are created in the loop.

I don't think there are any performance benefits, but it often makes the code much easier to think about and work with when you don't have to access multiple tables to get all of one song's information. Whatever works for you, really.

Last edited by Lokasenna; 05-26-2019 at 07:10 AM.
Lokasenna is offline   Reply With Quote
Old 05-29-2019, 04:09 PM   #9
tXShooter
Human being with feelings
 
tXShooter's Avatar
 
Join Date: Aug 2017
Posts: 294
Default

Quote:
Originally Posted by Lokasenna View Post
As far as your table structure, you've got one table with all of the titles, one with all of the singers, etc, but that information is related - Titles[1] corresponds to Singers[1], etc - so IMO it makes sense to have a table that keeps them together. AllDataForSong[1], AllDataForSong[2], etc. It can also help with the issue of nonexistent fields, above, since you only need the top-level table to start with and the rest are created in the loop.
My thinking was since I'm searching SongTextBoxScene = {}, that when a match is found, just use the same i to grab all of the corresponding information from the other tables, rather than to grab all of the information in the same table and process them however needed.
__________________
"But be ye doers of the word, and not hearers only, deceiving your own selves."
tXShooter is offline   Reply With Quote
Reply

Thread Tools
Display Modes

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

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

Forum Jump


All times are GMT -7. The time now is 11:42 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.