Garry's Mod Wiki

Tables - Hashmap Usage

Replacing Arrays with Hashmaps

In Lua, a table is, at its core, composed of two components.

  1. An array (otherwise referred to as a list or sequential table)
  2. A hashmap (otherwise referred to as a dictionary)

An array is a linear sequence of elements, so:

local array = {"A", "B", "C"}

As you can see were purely defining elements and letting the language determine the keys for us. This is useful in situations where you dont have a necessarily deterministic way of retrieving this data. If for instance we wanted to find the player named "Jeff" in the following array declaration.

--- --- Table contains a linear array of more tables --- local array = { --- Player index 1, since its the first in the table { "Goober" --- Name 21, --- Money }, --- Player index 2, and so on { "Pedro", 42, }, { "Jeff", 32, }, { "Nyaaa", 2442242424 } }

We could do the following:

for k, v in pairs(array) do if v[1] != "Jeff" then continue -- If their name; first key, is not Jeff, then skip to the next loop iteration end print("Jeff has $" .. v[2] .. "! Jeff has a player index of " .. k) end

But this seems a little sloggish, doesn't it? Why do we have to iterate over every single person in array to just find one! Although this may be performant, and work just fine in most cases, why is there not an easier way?... well there is! This is where we get into hashmaps!

Imagine this being structured a little differently, where instead of the player index being tied to where/when it was defined in the table, it is the name!

local hash = { --- Name = Money Goober = 21, Pedro, = 42, Jeff, = 32, Nyaaa, = 2442242424, }

This may seem like a benign change considering you can treat this nearly identically to the old solution to finding the player, but this opens up an entirely new way!

print("Jeff has $" .. hash["Jeff"] .. "!")

No need for iteration, makes life substantially better considering you now can find values without the need for a function to return them, or a temporary variable to set the found player too after iteration.

Notes

Dots vs Brackets

As you read above, we did hash["Jeff"] to retrieve Jeff from the table, but everywhere in the API when you get something from a table you do vgui.Create, not vgui["Create"]... Right? Well they're the same thing, the only difference is the syntax and one has a magic power. That power is allowing you to use an "Expression" (a piece of code that returns something) to index instead of a constant value, so.

local function GetCreate() return "Create" end local pnl = vgui[GetCreate()]("DPanel")

Yes, in this case, it is useless; however, this is extremely powerful, probably the most powerful thing about hashmaps entirely. Allowing you to dynamically index values makes it so you can use user input to find a value, get a player by its internal values you can only know at runtime, ect. You couldnt do the following could you?

local function GetCreate() return "Create" end local pnl = vgui.GetCreate()("DPanel")

It doesnt make sense!

ipairs

The ipairs iterator function is used the exact same way as page>Global.pairspage/>, except ipairs only iterates through sequential keys. Meaning, if it runs across nil in the sequential portion of a table, it will stop iterating.

local array = { [1] = "A", [2] = "B", -- [3] = nil, [4] = "D" } for k, v in ipairs(array) do print(v) end

This would output A B, not A B D, because it encountered nil on key 3. The workaround for this is

  1. Using pairs, which iterates over everything not nil, without stopping.
  2. Not allowing nil in the sequential table to begin with. Try table.remove