100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Arrays vs Dictionaries in Lua

See how Lua uses one table type to model both array-like sequences and dictionary-like maps, and learn the rules that keep each style predictable.

Tables & DataBeginner8 min readJul 10, 2026
Analogies

Arrays and Dictionaries: One Type, Two Styles

In many languages, arrays and dictionaries (or hash maps) are distinct types with different performance characteristics and APIs. Lua collapses both concepts into the single table type: an 'array' is simply a table whose keys are the consecutive integers 1, 2, 3, ... n, while a 'dictionary' (or 'hash map') is a table whose keys are arbitrary values such as strings. Internally, the Lua reference implementation actually optimizes this by storing the dense integer-keyed part in a contiguous array segment and everything else in a hash segment, but from the programmer's perspective it is one uniform table.

🏏

Cricket analogy: A Test match scorecard numbered ball-by-ball (array style) differs in feel from a player-of-the-tournament awards list keyed by category like 'Best Bowler' (dictionary style), yet both could live in the same physical scorebook, just as Lua stores both under one table type.

Array-Style Tables

An array-style table stores elements under consecutive integer keys starting at 1, such as local fruits = {'apple', 'banana', 'cherry'}, where fruits[1] is 'apple', fruits[2] is 'banana', and so on. The dedicated iterator ipairs(t) is built for this style: it walks i = 1, 2, 3, ... and stops as soon as it hits the first nil, which makes it fast and predictable but also means it will silently ignore anything after a gap. Array-style tables are the natural fit for ordered collections like queues, stacks, and lists where position matters more than named lookup.

🏏

Cricket analogy: A batting lineup where positions 1 through 11 must be filled in order is exactly array-style, and ipairs walking the lineup is like an umpire calling out batsmen strictly by number until the lineup sheet shows a blank slot, at which point he stops reading.

Dictionary-Style Tables

A dictionary-style table stores values under keys that are not a dense integer sequence -- most commonly strings -- such as local player = {name = 'Aria', level = 12, class = 'Mage'}. Fields are read with dot or bracket notation, and the whole table is walked with pairs(t), which visits every key regardless of type but makes no guarantee about the order of iteration. Dictionary-style tables are the natural fit for records and configuration objects, where each piece of data has a meaningful name rather than a meaningful position.

🏏

Cricket analogy: A player's profile card with named fields like battingAverage and bowlingStyle is dictionary-style, and pairs() reading it is like a broadcaster pulling up stats on screen in whatever order the graphics team queued them, not necessarily alphabetical or chronological.

Mixing Styles and Avoiding Pitfalls

A single table can legitimately combine both styles -- an array of items plus a couple of named metadata fields is common and safe, since ipairs simply ignores the non-integer keys and pairs sees everything. The real danger is creating holes inside the array part, for example {[1] = 'a', [3] = 'c'} with no key 2: ipairs will stop after index 1 and never see 'c', even though the key exists and pairs would find it. Because of this, code that expects array semantics should always build the sequence with table.insert or consecutive assignment, never by skipping indices.

🏏

Cricket analogy: A squad list that's mostly numbered 1-15 plus a named captain field is safe mixing, but leaving jersey number 7 vacant while filling 8 breaks a strict roll-call: the announcer (ipairs) stops at 6 and never reaches 8, even though the selectors (pairs) know player 8 exists.

lua
-- Array-style table
local fruits = {"apple", "banana", "cherry"}
for i, v in ipairs(fruits) do
  print(i, v)
end
-- 1  apple
-- 2  banana
-- 3  cherry

-- Dictionary-style table
local player = { name = "Aria", level = 12, class = "Mage" }
for k, v in pairs(player) do
  print(k, v)  -- order not guaranteed
end

-- Mixed table: safe
local inventory = {"sword", "shield", owner = "Aria"}
print(#inventory)          --> 2
print(inventory.owner)     --> Aria

-- DANGEROUS: a hole in the array part
local broken = {[1] = "a", [3] = "c"}
for i, v in ipairs(broken) do
  print(i, v)   -- prints only "1  a", never reaches index 3
end

ipairs silently stops at the first nil hole in the array part, even though the key beyond the hole still exists in the table. If you build sequences by assigning to arbitrary indices instead of using table.insert or consecutive assignment starting at 1, code that relies on ipairs or the # operator can quietly drop data with no error raised.

  • Lua uses one table type for both arrays (dense integer keys from 1) and dictionaries (arbitrary keys, often strings).
  • ipairs(t) iterates the array part in order and stops at the first nil; pairs(t) visits every key in unspecified order.
  • Array-style tables suit ordered data like queues and lists; dictionary-style tables suit named records and configuration.
  • Combining an array part with a few named fields in the same table is common and safe.
  • Creating a hole in the integer keys breaks ipairs and the # operator even though pairs still sees the data.
  • Always build sequences with table.insert or consecutive index assignment to avoid accidental holes.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#LuaStudyNotes#ArraysVsDictionariesInLua#Arrays#Dictionaries#Lua#One#DataStructures#StudyNotes#SkillVeris