---
url: /programming-fundamentals/tables-part-1.md
description: >-
  Arrays, dictionaries, nesting, iteration, and mutation in the table you will
  use everywhere.
---

# Tables, Part 1

*Written: {{ $frontmatter.date }}*

## Learning objectives

* You build arrays and read them with 1-based positions.
* You build dictionaries with named keys.
* You nest [tables](https://create.roblox.com/docs/luau/tables) inside tables.
* You measure arrays with `#` and grow or shrink them with `table.insert` and `table.remove`.
* You visit every entry with `for k, v in t`, change values, and delete keys with `nil`.

## Prerequisites

* Read [First Script](../getting-started/first-script.md). You can run a Script and read output.
* Read [Variables and Values](variables-and-values.md). You create variables with `local`.
* Read [Operators and Logic](operators-and-logic.md). You use `#` and comparisons.
* Read [Conditionals and Loops](conditionals-and-loops.md). You count with numeric `for`.
* Read [Functions and Scope](functions-and-scope.md). You call functions with arguments.

## Arrays keep an ordered list

A table is a container that holds many values at once. An array is a table used as an ordered list: first item, second item, third item. You build one with curly braces and commas.

Luau counts from one. The first item lives at position `1`, not `0`. This is called 1-based indexing, and it is the first thing programmers from other languages get wrong.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local fruits = { "apple", "pear", "plum" }
print(fruits[1])
print(fruits[2])
print(fruits[3])
```

```txt
apple
pear
plum
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds an array of three strings and stores it in `fruits`.
3. Line 3 prints position `1`, the first item, `"apple"`.
4. Line 4 prints position `2`, `"pear"`.
5. Line 5 prints position `3`, `"plum"`.

Square brackets with a position read one slot. You can also write through the same brackets to replace a slot or add a new one at the end.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local fruits = { "apple", "pear", "plum" }
fruits[2] = "peach"
print(fruits[2])
print(#fruits)
```

```txt
peach
3
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds the same three-item array.
3. Line 3 replaces position `2` with `"peach"`. The old `"pear"` is gone.
4. Line 4 prints the new position `2`, `"peach"`.
5. Line 5 prints `#fruits`. The `#` operator counts array items, so this prints `3`.

## Dictionaries label each value

A dictionary is a table where each value has a named key instead of a position. You build one with `key = value` pairs inside the braces. You read values with a dot, as in `player.name`, or with brackets, as in `player["name"]`.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local player = { name = "Maya", level = 7, ready = true }
print(player.name)
print(player.level)
print(player.ready)
```

```txt
Maya
7
true
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds a dictionary with three labeled values: `name`, `level`, and `ready`.
3. Line 3 prints the value behind the `name` key, `"Maya"`.
4. Line 4 prints the value behind `level`, `7`.
5. Line 5 prints the value behind `ready`, `true`.

Use arrays when order matters, like a lineup of winners. Use dictionaries when labels matter, like the stats of one player. One table can be both at once, but keep your tables one-shaped while you learn.

You add a new key by assigning to it and change a value by assigning to its key. Both use the same `=` you learned in [Variables and Values](variables-and-values.md).

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local player = { name = "Maya", level = 7 }
player.level = 8
player.team = "Falcons"
print(player.level)
print(player.team)
```

```txt
8
Falcons
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds a dictionary with `name` and `level`.
3. Line 3 overwrites `level` with `8`.
4. Line 4 creates a brand-new key, `team`, holding `"Falcons"`.
5. Lines 5 and 6 print `8` and `Falcons`.

## Tables nest inside tables

Values inside a table can be tables themselves. This is called nesting. A roster holds players, and each player holds stats. You reach inner values by chaining dots and brackets from the outside in.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local team = {
	name = "Falcons",
	members = { "Maya", "Rio", "Sam" },
	captain = { name = "Maya", level = 8 },
}
print(team.name)
print(team.members[1])
print(team.captain.level)
```

```txt
Falcons
Maya
8
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Lines 2 to 6 build `team`. Its `name` is a string, its `members` is an array, and its `captain` is a dictionary.
3. Line 7 prints `team.name`, the outer string `"Falcons"`.
4. Line 8 dives into the inner array and prints position `1`, `"Maya"`.
5. Line 9 dives into the inner dictionary and prints its `level`, `8`.

Read chains left to right. `team.captain.level` means: start at `team`, open `captain`, read `level`. When a chain prints `nil`, the break is usually one level up: some middle key is missing or misspelled.

## Grow and shrink arrays

Two library functions resize arrays. `table.insert` adds an item. `table.remove` takes one away and pulls later items down to close the gap. Both shift positions, so print the array after each change while you learn.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local fruits = { "apple", "pear" }
table.insert(fruits, "plum")
print(fruits[3])
print(#fruits)
table.remove(fruits, 1)
print(fruits[1])
print(#fruits)
```

```txt
plum
3
pear
2
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds a two-item array.
3. Line 3 appends `"plum"` to the end.
4. Line 4 prints the new position `3`, `"plum"`.
5. Line 5 prints the new length, `3`.
6. Line 6 removes position `1`. `"pear"` slides from position `2` down to position `1`, and `"plum"` slides to position `2`.
7. Line 7 prints the new position `1`, `"pear"`.
8. Line 8 prints the new length, `2`.

Give `table.insert` a position to wedge an item into the middle, as in `table.insert(fruits, 2, "peach")`. Everything from that position onward shifts up by one. With no position, `table.remove` pops the last item.

## Visit every entry with a loop

A `for k, v in t` loop visits each entry once and hands you the key and the value. For arrays the key is the position. For dictionaries the key is the label. Dictionary order is not guaranteed, so never rely on it.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local fruits = { "apple", "peach", "plum" }
for index, value in fruits do
	print(index)
	print(value)
end
```

```txt
1
apple
2
peach
3
plum
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds a three-item array.
3. Line 3 starts the loop. Each round puts the position in `index` and the item in `value`.
4. Line 4 prints the position.
5. Line 5 prints the item.
6. Line 6 ends the loop body. Arrays visit in order, so the output runs `1` apple `2` peach `3` plum.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local player = { name = "Maya", level = 8 }
for key, value in player do
	print(key)
	print(value)
end
```

```txt
name
Maya
level
8
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds a two-key dictionary.
3. Line 3 starts the loop. Each round puts the label in `key` and the stored value in `value`.
4. Line 4 prints the key.
5. Line 5 prints the value.
6. Line 6 ends the loop body. Both entries print. Their order may vary, and that is normal for dictionaries.

You can change values mid-loop through the original table. Below, each round writes back through `levels[index]`.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local levels = { 1, 2, 3 }
for index, value in levels do
	levels[index] = value * 10
end
print(levels[1])
print(levels[2])
print(levels[3])
```

```txt
10
20
30
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds `{ 1, 2, 3 }`.
3. Line 3 loops with `index` and `value`.
4. Line 4 multiplies each value by `10` and writes it back into the same slot. This changes the table itself, not just the loop variable.
5. Line 5 ends the loop body.
6. Lines 6 to 8 print `10`, `20`, `30`.

## Delete dictionary keys with nil

Setting a dictionary key to [nil](https://create.roblox.com/docs/luau/nil) erases the entry. The key disappears. Reading it afterward gives `nil`, which means "nothing here". Never store `nil` inside an array on purpose. It punches a hole that `#` and loops cannot count reliably.

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local player = { name = "Maya", title = "Rookie" }
print(player.title)
player.title = nil
print(player.title)
```

```txt
Rookie
nil
```

Walkthrough:

1. Line 1 turns on strict mode.
2. Line 2 builds a dictionary with `name` and `title`.
3. Line 3 prints `"Rookie"`.
4. Line 4 assigns `nil` to `title`. This deletes the key entirely.
5. Line 5 prints `nil` because the key no longer exists.

## Common mistakes

**Reading position zero.**

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local fruits = { "apple", "pear", "plum" }
print(fruits[0])
```

```txt
nil
```

This prints `nil` because arrays start at `1`. Fix it by reading position `1` for the first item. Run the fix the same way.

```luau
--!strict
local fruits = { "apple", "pear", "plum" }
print(fruits[1])
```

```txt
apple
```

**Trusting `#` on a table with holes.**

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local scores = { 10, 20, 30 }
scores[2] = nil
print(#scores)
```

```txt
Unreliable: Luau may print 1 or 3 here because the hole in the middle breaks #.
```

The length is unreliable now because the middle is missing. Fix it by removing with `table.remove`, which closes the gap. Run the fix the same way.

```luau
--!strict
local scores = { 10, 20, 30 }
table.remove(scores, 2)
print(#scores)
print(scores[2])
```

```txt
2
30
```

**Forgetting that `table.remove` shifts positions.**

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local fruits = { "apple", "pear", "plum" }
table.remove(fruits, 1)
print(fruits[3])
```

```txt
nil
```

This prints `nil` because `"plum"` slid from position `3` to position `2`. Fix it by reading the new positions after removal. Run the fix the same way.

```luau
--!strict
local fruits = { "apple", "pear", "plum" }
table.remove(fruits, 1)
print(fruits[1])
print(fruits[2])
```

```txt
pear
plum
```

**Relying on dictionary order.**

Run this: put it in a Script in ServerScriptService and press Play.

```luau
--!strict
local player = { name = "Maya", level = 8 }
for key, value in player do
	print(key)
	print(value)
end
```

```txt
name
Maya
level
8
```

You may see `level` first instead. Beginners expect `name` always first, but Luau makes no such promise for dictionaries. Treat dictionary order as random and never build logic on it. Your output above is correct as long as both entries print.

## Exercises

Run every solution below in a Script in ServerScriptService and press Play. Try each exercise yourself before you open its solution.

1. Build an array of three game names. Print the first and the last with brackets.

::: details Solution 1

```luau
--!strict
local games = { "Tag", "Obby", "Tycoon" }
print(games[1])
print(games[3])
```

```txt
Tag
Tycoon
```

:::

2. Build a dictionary for a sword with keys `name` and `damage`. Print both values with dots.

::: details Solution 2

```luau
--!strict
local sword = { name = "Ember", damage = 12 }
print(sword.name)
print(sword.damage)
```

```txt
Ember
12
```

:::

3. Start from `{ "apple", "pear" }`. Insert `"plum"` at the end and remove position `1`. Print the two remaining items by position and the final length.

::: details Solution 3

```luau
--!strict
local fruits = { "apple", "pear" }
table.insert(fruits, "plum")
table.remove(fruits, 1)
print(fruits[1])
print(fruits[2])
print(#fruits)
```

```txt
pear
plum
2
```

After removal, `"pear"` sits at position `1` and `"plum"` at position `2`.

:::

4. Build a nested `team` table with a `name` and a two-item `members` array. Print the team name, then loop over `members` with `for k, v in t` and print each member.

::: details Solution 4

```luau
--!strict
local team = { name = "Falcons", members = { "Maya", "Rio" } }
print(team.name)
for index, member in team.members do
	print(index)
	print(member)
end
```

```txt
Falcons
1
Maya
2
Rio
```

Line 3 loops over the inner array `team.members`, not the outer table.

:::

## Summary

You built arrays with 1-based positions and dictionaries with named keys. You nested tables, measured arrays with `#`, resized them with `table.insert` and `table.remove`, visited entries with `for k, v in t`, changed values in place, and deleted dictionary keys by assigning `nil`.

Next: [Strings, Numbers, and Nil](strings-numbers-nil.md).
