How to index a table automatically and loop it in ipairs while keeping all data? - loops

Table:
localization_strings = {
string_1 = "Text Here",
string_2 = "Some More Text Here",
string_3 = "More Text"
}
This is obviously not the whole table, but just a small sample. The real table is over 500+ lines. The reason I don't just redo the table is because other functions reference it and I don't have access to those files to fix them, so I have to find a work around. Also, because it would quite tedious work and can cause problems with other codes.
I have made 2 attempts at solving this problem, but I can only get one of the values I want (incorrect terminology, I think) and I need both as 1 is display text and 1 is data for a function call.
Attempts:
-- Attempt #1
-- Gives me the string_#'s but not the "Text"...which I need, as I want to display the text via another function
LocalizationUnorderedOpts = {}
LocalizationOpts = {}
for n,unordered_names in pairs(localization_strings) do
if (unordered_names) then
table.insert( LocalizationUnorderedOpts, n)
end
end
io.write(tostring(LocalizationUnorderedOpts) .. "\n")
table.sort(LocalizationUnorderedOpts)
for i,n in ipairs(LocalizationUnorderedOpts) do
if (n) then
io.write(tostring(i))
table.insert( LocalizationOpts, { text = tostring(LocalizationUnorderedOpts[i]), callback = function_pointer_does_not_matter, data = i } )
end
end
-- Attempt #2
-- Gives me the "Text" but not the string_#'s...which I need to as data to the callback to another function (via function pointer)
LocalizationUnorderedOpts = {}
LocalizationOpts = {}
for n,unordered_names in pairs(localization_strings) do
if (unordered_names) then
table.insert( LocalizationUnorderedOpts, localization_strings[n])
end
end
io.write(tostring(LocalizationUnorderedOpts) .. "\n")
table.sort(LocalizationUnorderedOpts)
for i,n in ipairs(LocalizationUnorderedOpts) do
if (n) then
io.write(tostring(i))
table.insert( LocalizationOpts, { text = tostring(LocalizationUnorderedOpts[i]), callback = function_pointer_does_not_matter, data = i } )
end
end

If I understand it correctly, you need to sort the non-array table. Your first attempt has done most of the work: build another table, which has the values the same as the keys in the original table.
What's left is how to get the original values like "Text Here", for that you need to index the original table:
for k, v in ipairs(LocalizationUnorderedOpts) do
print(v) --original key
print(localization_strings[v]) --original value
end

Related

Table[1] returning nil when table does exist and has values

local mapSpawnsData = {}
local JSONData = file.Read(filePath) -- read file as json text
mapSpawnsData = util.JSONToTable(JSONData) -- convert JSON to table
print("TABLE:")
PrintTable(mapSpawnsData)
print("TABLE[1]:")
print(tostring(mapSpawnsData[1]))
This is a script for a game called garrysmod. PrintTable() is a function I can call included in the game.
The code snippet I included returns this: (Same output if I remove the tosring())
TABLE:
gm_construct:
1 = -1303.524902 167.472397 -44.081600
2 = 1250.890137 331.746185 -44.081600
3 = 674.012085 223.775604 -32.148102
TABLE[1]:
nil
I expected to get back "gm_construct". What am I missing here?
What you didn't notice is that PrintTable printed gm_construct: first and then 1 = .
That means the table actually contains only gm_construct key with a table with 3 keys.
To be able to always read PrintTable output properly look at the ident. 1 = is tabulated to the right once, this means they are keys of something nested in the table you initially printed.
If this still confuses you, try to run this:
for k,v in pairs(mapSpawnsData) do
print(k, "=", v)
end
This won't print nested content.
To make your code work do mapSpawnsData.gm_construct[1] or mapSpawnsData["gm_construct"][1]

Finding the Value of an Object Element within an Array

I have a table that im inserting into , The table holds Plate Values
How would i go about iterating over the table to return the following as true
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end
My current table insert
for k, v in pairs(returnVehicleData) do
platesAvailable = v.plate
newTable = {['plate'] = platesAvailable}
table.insert(vehiclePlateTable, newTable)
end
The data thats being returned
[{"plate":"47QVS009"},{"plate":"86KIE632"}]
I want to check the subsequent value after the : on both objects.
I ended up doing this and getting it to work
newValueTable = {}
for Key = 1, #tabledPlate, 1 do
table.insert(newValueTable, tabledPlate[Key].plate)
end
Because the elements are part of an even bigger table of data , they dont actually return as a string. But thank you for the suggestion Coordinate Newton

LUA script: Nested loop inserts only last item

I have two loops, a main loop and a sub-loop inside the main loop. Both loops populate the same table (and sub-table). But for some reason the sub-loop only stores the last added item in the nested table.
For instance, main group 1 has sub-groups stored as 1-9:
printTable(data[1][subItems][1]) -- returns error (index nil value)
printTable(data[1][subItems][9]) -- dumps table to console
If I break the sub-loop after one iteration then data[1][subItems][1] contains data
for i=startId, endId, 10 do
items = loadItems(i)
data[i] = {['items'] = items}
for x=i+1, i+10-1 do
subItems = loadItems(x)
print('adding items to sub-group: '..x..' for main group: '..i)
data[i]['subItems'] = {}
data[i]['subItems'][x] = {['items'] = subItems}
end
end
end
Since I'm printing some debug info inside the sub-loop, I know that, the code is being executed. And I know that loadItems(x) is getting the data on each iteration, because if i dump the loadItems(x) to console in the sub-loop, all is there on each iteration.
What is this wizardry?
Silly me, how could I miss it!
The answer is of course to move the data[i][subItems] = {} outside the sub-loop:
for i=startId, endId, 10 do
items = loadItems(i)
data[i] = {['items'] = items}
data[i]['subItems'] = {} <----------------------------------------------
for x=i+1, i+10-1 do
subItems = loadItems(x)
print('adding items to sub-group: '..x..' for main group: '..i)
data[i]['subItems'][x] = {['items'] = subItems}
end
end
end

Csv file to a Lua table and access the lines as new table or function()

Currently my code have simple tables containing the data needed for each object like this:
infantry = {class = "army", type = "human", power = 2}
cavalry = {class = "panzer", type = "motorized", power = 12}
battleship = {class = "navy", type = "motorized", power = 256}
I use the tables names as identifiers in various functions to have their values processed one by one as a function that is simply called to have access to the values.
Now I want to have this data stored in a spreadsheet (csv file) instead that looks something like this:
Name class type power
Infantry army human 2
Cavalry panzer motorized 12
Battleship navy motorized 256
The spreadsheet will not have more than 50 lines and I want to be able to increase columns in the future.
Tried a couple approaches from similar situation I found here but due to lacking skills I failed to access any values from the nested table. I think this is because I don't fully understand how the tables structure are after reading each line from the csv file to the table and therefore fail to print any values at all.
If there is a way to get the name,class,type,power from the table and use that line just as my old simple tables, I would appreciate having a educational example presented. Another approach could be to declare new tables from the csv that behaves exactly like my old simple tables, line by line from the csv file. I don't know if this is doable.
Using Lua 5.1
You can read the csv file in as a string . i will use a multi line string here to represent the csv.
gmatch with pattern [^\n]+ will return each row of the csv.
gmatch with pattern [^,]+ will return the value of each column from our given row.
if more rows or columns are added or if the columns are moved around we will still reliably convert then information as long as the first row has the header information.
The only column that can not move is the first one the Name column if that is moved it will change the key used to store the row in to the table.
Using gmatch and 2 patterns, [^,]+ and [^\n]+, you can separate the string into each row and column of the csv. Comments in the following code:
local csv = [[
Name,class,type,power
Infantry,army,human,2
Cavalry,panzer,motorized,12
Battleship,navy,motorized,256
]]
local items = {} -- Store our values here
local headers = {} --
local first = true
for line in csv:gmatch("[^\n]+") do
if first then -- this is to handle the first line and capture our headers.
local count = 1
for header in line:gmatch("[^,]+") do
headers[count] = header
count = count + 1
end
first = false -- set first to false to switch off the header block
else
local name
local i = 2 -- We start at 2 because we wont be increment for the header
for field in line:gmatch("[^,]+") do
name = name or field -- check if we know the name of our row
if items[name] then -- if the name is already in the items table then this is a field
items[name][headers[i]] = field -- assign our value at the header in the table with the given name.
i = i + 1
else -- if the name is not in the table we create a new index for it
items[name] = {}
end
end
end
end
Here is how you can load a csv using the I/O library:
-- Example of how to load the csv.
path = "some\\path\\to\\file.csv"
local f = assert(io.open(path))
local csv = f:read("*all")
f:close()
Alternative you can use io.lines(path) which would take the place of csv:gmatch("[^\n]+") in the for loop sections as well.
Here is an example of using the resulting table:
-- print table out
print("items = {")
for name, item in pairs(items) do
print(" " .. name .. " = { ")
for field, value in pairs(item) do
print(" " .. field .. " = ".. value .. ",")
end
print(" },")
end
print("}")
The output:
items = {
Infantry = {
type = human,
class = army,
power = 2,
},
Battleship = {
type = motorized,
class = navy,
power = 256,
},
Cavalry = {
type = motorized,
class = panzer,
power = 12,
},
}

Nested loop conditional

Supposed I have this table:
TDID TDLINE
F04 04-AA
F04 04-BB <-- call a function
F05 05-AA
F05 05-BB <-- call a function
F06 06-AA <-- call a function
I would like to call a function while the TDID field is not the same as the previous one. I have the code below, it works but somehow it's not perfectly works (it missed the last row):
LOOP AT lines ASSIGNING <fs1>.
IF <fs2> IS INITIAL.
<fs2> = <fs1>.
ELSE.
li_line-tdline = <fs2>-tdline.
APPEND li_line.
IF <fs1>-tdid NE <fs2>-tdid.
li_thead-tdid = <fs2>-tdid.
CALL FUNCTION 'SAVE_TEXT'
EXPORTING
header = li_thead
savemode_direct = 'X'
TABLES
lines = li_line
CLEAR: li_thead,
li_line.
FREE: li_thead,
li_line.
ENDIF.
ENDIF.
ENDLOOP.
ANSWER
Thank you to vwegert for the answer:
LOOP AT lines ASSIGNING <fs1>.
AT NEW tdid.
REFRESH li_thead.
REFRESH li_line.
li_thead-tdid = <fs1>-tdid.
APPEND li_thead.
ENDAT.
li_line-tdline = <fs1>-tdline.
APPEND li_line.
AT END OF tdid.
CALL FUNCTION 'SAVE_TEXT'
EXPORTING
header = li_thead
savemode_direct = 'X'
TABLES
lines = li_line
ENDAT.
ENDLOOP.
Assuming that the table is sorted by TDID and no field left of TDID changes more frequently than TDID:
LOOP AT lines ASSIGNING <fs1>.
AT NEW tdid.
REFRESH some_other_tab.
ENDAT.
APPEND <fs1> TO some_other_tab.
AT END OF tdid.
CALL FUNCTION ...
ENDAT.
ENDLOOP.
The unpredictability as mentioned by vwegert comes because the characters fields next to the field on which Control statement is applied are converted to asterisks(*). If you want to use these values in the control statement make sure you copy the values in a temporary table and loop on it instead of the original internal table and use the values using READ on the original internal table. Also keep in mind that control statement considers all columns to the left of the column being used in the statement for it's condition.

Resources