Revision Difference
List-Styled_Tables#562550
<cat>Dev.Lua</cat>
<title>Tables - Hashmap Usage</title>
# 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:
```lua
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.
```lua
---
--- 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⤶
2442242424,⤶
}
}
```
We could do the following:
```lua
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!
```lua
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!
```lua
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.
```lua
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?
```lua
local function GetCreate()
return "Create"
end
local pnl = vgui.GetCreate()("DPanel")
```
It doesnt make sense!
## ipairs
The <page>Global.ipairs</page> iterator function is used the exact same way as <page>Global.pairs<page/>, except <page>Global.ipairs</page> only iterates through sequential keys. Meaning, if it runs across `nil` in the sequential portion of a table, it will stop iterating.
```lua
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 <page>Global.pairs</page>, which iterates over **everything** not `nil`, without stopping.
2. Not allowing `nil` in the sequential table to begin with. Try <page>table.remove</page>