GDScript Arrays & Dictionaries Cheat Sheet
Quick-reference for Godot 4 collections. Each section includes copy-ready snippets with inline output comments for inventory, enemy tracking, and game state storage.
Array Basics
GDScript arrays are dynamic, ordered, and can hold mixed types (unless typed).
var inventory := ["sword", "shield", "potion"]
inventory[0] # => "sword"
inventory[-1] # => "potion" (last element)
inventory.size() # => 3var enemies := [] # empty untyped
var scores: Array[int] = [] # empty typed
var grid := [[0, 0], [0, 0]] # 2D arrayvar loot := []
loot.is_empty() # => true
# Or use shorthand:
if not loot:
print("No loot found")Adding and Removing Elements
Modify arrays with append, push, pop, insert, and erase.
var bag := ["potion"]
bag.append("key") # => ["potion", "key"]
bag.push_back("arrow") # => ["potion", "key", "arrow"]
bag.push_front("shield") # => ["shield", "potion", "key", "arrow"]var queue := ["goblin", "dragon"]
queue.insert(1, "skeleton")
# queue => ["goblin", "skeleton", "dragon"]var stack := ["A", "B", "C"]
var last := stack.pop_back() # last => "C", stack => ["A", "B"]
var first := stack.pop_front() # first => "A", stack => ["B"]var items := ["sword", "bow", "sword", "potion"]
items.erase("sword") # removes FIRST "sword"
# items => ["bow", "sword", "potion"]
items.remove_at(0) # removes index 0
# items => ["sword", "potion"]Searching and Checking
Find elements, check membership, and count occurrences.
var inventory := ["sword", "potion", "key"]
inventory.has("potion") # => true
inventory.has("axe") # => false
inventory.find("key") # => 2 (index)
inventory.find("axe") # => -1 (not found)var loot := ["gold", "gold", "gem", "gold"]
loot.count("gold") # => 3
loot.count("gem") # => 1var log := ["hit", "miss", "hit", "crit"]
log.rfind("hit") # => 2 (last occurrence)Slicing and Duplicating
Extract portions of arrays and create independent copies.
var all_enemies := ["A", "B", "C", "D", "E"]
var first_three := all_enemies.slice(0, 3)
# first_three => ["A", "B", "C"]
var last_two := all_enemies.slice(-2)
# last_two => ["D", "E"]var original := [1, 2, [3, 4]]
var shallow := original.duplicate() # nested arrays still shared
var deep := original.duplicate(true) # fully independent copyvar spawn_points := [Vector2(100, 100), Vector2(500, 200), Vector2(300, 400)]
var pos := spawn_points.pick_random()
# pos => one random spawn pointSorting and Filtering
Sort arrays in place and filter with custom callables.
var scores := [50, 10, 90, 30]
scores.sort()
# scores => [10, 30, 50, 90]# Sort enemies by distance to player
var enemies: Array[Node2D] = get_tree().get_nodes_in_group("enemies")
enemies.sort_custom(func(a, b):
return a.global_position.distance_squared_to(player_pos) < \
b.global_position.distance_squared_to(player_pos)
)
# enemies[0] is now the closestvar items := [
{"name": "sword", "damage": 10},
{"name": "bow", "damage": 7},
{"name": "staff", "damage": 15},
]
# Filter: keep only items with damage > 8
var strong := items.filter(func(item): return item.damage > 8)
# strong => [{"name": "sword", ...}, {"name": "staff", ...}]
# Map: extract just the names
var names := items.map(func(item): return item.name)
# names => ["sword", "bow", "staff"]Typed Arrays
Typed arrays enforce element types at runtime. Use them for type safety and Inspector integration.
var hp_values: Array[int] = [100, 80, 120]
var enemy_names: Array[String] = ["Goblin", "Skeleton"]
var waypoints: Array[Vector2] = [Vector2.ZERO, Vector2(100, 50)]var enemies: Array[Enemy] = []
func register_enemy(e: Enemy) -> void:
enemies.append(e) # type-checked at runtime
# This would error at runtime:
# enemies.append("not an enemy")@export var patrol_points: Array[Vector2] = []
@export var loot_table: Array[ItemData] = []
@export var wave_configs: Array[WaveConfig] = []
# Inspector shows type-specific UI for each elementTyped arrays show proper editors in the Inspector — Vector2 fields, Resource pickers, etc.
Dictionary Basics
Dictionaries are key-value maps. Keys can be any type (usually String or int).
var player := {
"name": "Hero",
"hp": 100,
"position": Vector2(50, 50),
}
player["name"] # => "Hero"
player["hp"] # => 100
player.get("mana", 0) # => 0 (default if key missing)var stats := {}
stats["strength"] = 10
stats["defense"] = 5
stats["strength"] += 2 # => 12var inventory := {"sword": 1, "potion": 3}
inventory.has("sword") # => true
inventory.has("axe") # => false
# Safe access with get()
var count := inventory.get("axe", 0) # => 0 (default)Dictionary Iteration
Loop over keys, values, or both using keys(), values(), or direct iteration.
var scores := {"player_1": 100, "player_2": 85, "player_3": 92}
for player_name in scores:
print("%s: %d" % [player_name, scores[player_name]])
# player_1: 100
# player_2: 85
# player_3: 92var gear := {"weapon": "sword", "armor": "chain", "ring": "gold"}
gear.keys() # => ["weapon", "armor", "ring"]
gear.values() # => ["sword", "chain", "gold"]var enemy_hp := {"goblin": 30, "skeleton": 50, "dragon": 200}
for enemy in enemy_hp:
var hp := enemy_hp[enemy]
if hp > 100:
print("%s is a boss!" % enemy) # => "dragon is a boss!"Dictionary Operations
Merge, erase, duplicate, and compare dictionaries.
var base_stats := {"hp": 100, "atk": 10, "def": 5}
var bonus := {"atk": 3, "speed": 2}
base_stats.merge(bonus)
# base_stats => {"hp": 100, "atk": 10, "def": 5, "speed": 2}
# Note: existing keys are NOT overwritten by default
base_stats.merge(bonus, true) # true = overwrite existing
# base_stats => {"hp": 100, "atk": 3, "def": 5, "speed": 2}var inventory := {"sword": 1, "potion": 3, "key": 1}
inventory.erase("key")
# inventory => {"sword": 1, "potion": 3}var original := {"items": ["sword", "bow"], "gold": 100}
var copy := original.duplicate() # shallow copy
var deep := original.duplicate(true) # deep copy (nested arrays too)PackedArrays
PackedArrays store a single type contiguously in memory. Much faster than generic Array for large datasets.
var positions := PackedVector2Array()
var colors := PackedColorArray()
var ids := PackedInt32Array()
var names := PackedStringArray()
var mesh_data := PackedFloat32Array()var points := PackedVector2Array([
Vector2(0, 0),
Vector2(100, 0),
Vector2(100, 100),
Vector2(0, 100),
])
# Use for draw_polygon, collision shapes, etc.
draw_polygon(points, PackedColorArray([Color.RED]))var path := PackedVector2Array()
path.append(Vector2(0, 0))
path.append(Vector2(50, 25))
path.append(Vector2(100, 0))
path.size() # => 3
path[1] # => (50, 25)PackedArrays are faster for iteration and use less memory than Array. Use them for paths, vertices, and large data sets.
Common Collection Pitfalls
Quick fixes for the most frequent array and dictionary bugs.
# BUG: removing elements while iterating skips items
for enemy in enemies:
if enemy.is_dead():
enemies.erase(enemy) # index shifts — skips next!
# FIX: iterate a copy, or iterate backwards
for enemy in enemies.duplicate():
if enemy.is_dead():
enemies.erase(enemy)# BUG: both point to same array
var a := [1, 2, 3]
var b = a
b.append(4)
# a => [1, 2, 3, 4] — a was modified too!
# FIX: use duplicate()
var c := a.duplicate()
c.append(5)
# a is unchanged# BUG: accessing missing key crashes
var data := {"name": "Goblin"}
data["hp"] # KeyError!
# FIX: use get() with default
var hp := data.get("hp", 100) # => 100
# Or check first
if data.has("hp"):
print(data["hp"])Always use .get(key, default) or .has(key) when the key might not exist.
Can you write this from memory?
Declare an array called upgrade_names containing the strings 'speed', 'damage', and 'health'.