Lua has one data structure. Not one per job — one, total. It is called a table, and it does the work of arrays, dictionaries, objects, classes, modules, and namespaces. This activity uses it as a list. The next two use the same thing for entirely different purposes.
Positions start at 1. Not 0. This is the single most quoted fact about Lua and the one most likely to trip you.
local crew = { "Ada", "Bo", "Cy" }
print(crew[1])print(crew[3])print(#crew)print(crew[4])AdaCy3nil#crew counts the items — the same # you used on strings in Activity 1.3. Because counting starts at 1, the last item is at crew[#crew], not crew[#crew - 1], which is one of the few places 1-indexing is genuinely tidier.
And reading past the end is not an error. You get nil, exactly like the misspelled variable in Activity 1.2. Same silence, same class of bug.
ipairs hands you the position and the value together, in order, from 1 until it hits a gap.
local crew = { "Ada", "Bo", "Cy" }
for i, name in ipairs(crew) do print(i, name)end1 Ada2 Bo3 CyThis is the fourth loop form from Activity 1.8, and ipairs is an ordinary function returning a closure — nothing about it is built into the language. If you do not need the position, the convention is to name it _, which is just a variable name that means "ignore me."
local crew = { "Ada", "Bo", "Cy" }
table.insert(crew, "Dee")table.insert(crew, 1, "Zed")print(table.concat(crew, ", "))
table.remove(crew)table.remove(crew, 1)print(table.concat(crew, ", "))Zed, Ada, Bo, Cy, DeeAda, Bo, Cytable.insert(t, v) appends. Give it a position first and it inserts there, shuffling everything else along. table.remove(t) takes the last one off; with a position it removes that one and closes the gap. table.concat joins a list into a string, which is the tidiest way to look at one.
table.sort rearranges the list in place — it returns nothing, so local s = table.sort(t) gives you nil and is a common first mistake.
local scores = { 40, 12, 87, 3 }
table.sort(scores)print(table.concat(scores, " "))
table.sort(scores, function(a, b) return a > b end)print(table.concat(scores, " "))3 12 40 8787 40 12 3The second call passes a function that answers should a come before b? — and this is Activity 1.7's "functions are values" doing real work. Swap > for < and the order flips.
Removing items while looping forwards is broken, and it fails quietly. Every time you remove one, everything after it slides down a position — but the loop counter keeps going up, so it steps straight over an item.
local nums = { 2, 4, 5, 6 }
for i = 1, #nums do local value = nums[i] if value ~= nil and value % 2 == 0 then table.remove(nums, i) endend
print(table.concat(nums, " "))4 5local nums = { 2, 4, 5, 6 }
for i = #nums, 1, -1 do if nums[i] % 2 == 0 then table.remove(nums, i) endend
print(table.concat(nums, " "))5Both were asked to remove every even number. The forwards version left a 4 behind. It removed the 2, everything shifted down, and the 4 landed in the position the loop had already passed.
Loop backwards whenever you remove. Positions after the one you removed are the only ones that move, and going backwards you have already dealt with those. This is not a style preference — it is the difference between a bullet disappearing and a bullet surviving, and in Unit 2 you will write this exact loop.
Note the forwards version also needed a value ~= nil guard to avoid crashing outright, because the list got shorter than the count the loop started with. The backwards version needs no guard at all.
Start a fresh main.lua. You are keeping a crew roster, then filtering a list of scores.
ipairs.-- main.lua - Skynest crew roster
local crew = { "Ada", "Bo", "Cy" }
print("Crew size:", #crew)print("First:", crew[1])print("Last:", crew[#crew])
for i, name in ipairs(crew) do print(i, name)endCrew size: 3First: AdaLast: Cy1 Ada2 Bo3 Cytable.concat.-- main.lua - Skynest crew roster
local crew = { "Ada", "Bo", "Cy" }
print("Crew size:", #crew)print("First:", crew[1])print("Last:", crew[#crew])
for i, name in ipairs(crew) do print(i, name)end
table.insert(crew, "Dee")table.insert(crew, 1, "Zed")print("Roster:", table.concat(crew, ", "))
table.remove(crew, 1)print("After launch:", table.concat(crew, ", "))Crew size: 3First: AdaLast: Cy1 Ada2 Bo3 CyRoster: Zed, Ada, Bo, Cy, DeeAfter launch: Ada, Bo, Cy, Dee{ 12, 40, 7, 88, 5 }.-- main.lua - Skynest crew roster
local crew = { "Ada", "Bo", "Cy" }
print("Crew size:", #crew)print("First:", crew[1])print("Last:", crew[#crew])
for i, name in ipairs(crew) do print(i, name)end
table.insert(crew, "Dee")table.insert(crew, 1, "Zed")print("Roster:", table.concat(crew, ", "))
table.remove(crew, 1)print("After launch:", table.concat(crew, ", "))
local scores = { 12, 40, 7, 88, 5 }
for i = #scores, 1, -1 do if scores[i] < 10 then table.remove(scores, i) endend
print("Qualified:", table.concat(scores, " "))contains(list, value) returning true or false. Then make it return the position instead, or nil — which is more useful and costs nothing extra.crew[2] = nil and then print #crew and loop it with ipairs. A hole in the middle of a list breaks both — which is exactly why table.remove exists instead.Your finished file should produce this.
Crew size: 3First: AdaLast: Cy1 Ada2 Bo3 CyRoster: Zed, Ada, Bo, Cy, DeeAfter launch: Ada, Bo, Cy, DeeQualified: 12 40 88Qualified is 12 40 88 — both the 7 and the 5 are gone. If either survived, your loop was going forwards.