Revision Difference
Tables:_Bad_Habits#529025
<cat>Dev.Lua</cat>
Manipulating tables in an inefficient way is so common that instead of complaining, I am publishing a guide.
Feel free to share this guide all around the Lua programming community, especially beginners!
The examples below do not necessarily work as they are shown. They are only given for a teaching purpose.
## Example 1
`Description:` This example restricts the vehicle classes usable by some teams.
This example shows what not to do because using useless loops is inefficient.
```
-- EXAMPLE OF BAD CODE: DO NOT USE
local RestrictedTeams = {
TEAM_COOK,
TEAM_CITIZEN,
TEAM_MOB,
}
local AllowedVehicles = {
"prop_vehicle_jeep",
"prop_vehicle_airboat",
"prop_vehicle_custom1",
"prop_vehicle_custom4",
"prop_vehicle_custom5"
}
hook.Add( "CanPlayerEnterVehicle", "restrict vehicles", function( ply, veh )
if table.HasValue( RestrictedTeams, ply:Team() ) then
if not table.HasValue( AllowedVehicles, veh:GetClass() ) then
return false
end
end
end )
```
Now let's expand every call of <page>table.HasValue</page> as a loop. This basically translates the function to its meaning.
```
-- EXPANSION OF BAD CODE: DO NOT USE
hook.Add( "CanPlayerEnterVehicle", "restrict vehicles", function( ply, veh )
local Value = ply:Team()
local HasValue = false
for k, v in pairs( RestrictedTeams ) do
if ( v == Value ) then
HasValue = true
break
end
end
if HasValue then
local Value = veh:GetClass()
local HasValue = false
for k, v in pairs( AllowedVehicles ) do
if ( v == Value ) then
HasValue = true
break
end
end
if not HasValue then
return false
end
end
end )
```
Now you see clearly that you loop over tables by using <page>table.HasValue</page>. In 99% of situations, checking the presence of a **value** in a table is a waste of CPU time. Using this method is very bad for small tables, and it is insane for large tables.
The next code shows how to make your tables efficient, with no loop and no function calls! It only relies on the presence of a **key** (never a **value**): that is what keys are designed for.
```
-- EXAMPLE OF GOOD CODE
local RestrictedTeams = {
[TEAM_COOK] = true,
[TEAM_CITIZEN] = true,
[TEAM_MOB] = true
}
local AllowedVehicles = {
prop_vehicle_jeep = true,
prop_vehicle_airboat = true,
prop_vehicle_custom1 = true,
prop_vehicle_custom4 = true,
prop_vehicle_custom5 = true
}
hook.Add( "CanPlayerEnterVehicle", "restrict vehicles", function( ply, veh )
if RestrictedTeams[ply:Team()] then
if not AllowedVehicles[veh:GetClass()] then⤶
return false⤶
end⤶
if RestrictedTeams[ply:Team()] and not AllowedVehicles[veh:GetClass()] then
return false⤶
end
end )
```
We just check the value inside the table directly by using a key! If the value is **true** then the key is in the table, otherwise it is **nil**, which is equivalent to **false** in a condition. This process is very inexpensive and it is worth it.
This example uses tables containing keys that are numbers or strings, but they also can be entities, vectors, angles, etc.
## Identical keys
Stay aware from something though: objects can have identical values but still be distinct!
```
local angle1 = Angle( 90, 0, 0 )
local angle2 = Angle( 90, 0, 0 )
print( "angles are same (1):", ( angle1 == angle2 ) )
angle2.p = 180
print( "angles are same (2):", ( angle1 == angle2 ) )
--[[
Output:
angles are same (1): true
angles are same (2): false
]]
```
Despite these angles being identical, they are 2 distinct objects in memory as you can see!
```
local vector1 = Vector( 100, 0, 0 )
local vector2 = Vector( 100, 0, 0 )
local TestTable = {}
TestTable[ vector1 ] = true
TestTable[ vector2 ] = true
for vector in pairs( TestTable ) do
print( "vector is", vector )
end
--[[
Output:
vector is 100 0 0
vector is 100 0 0
]]
```
For instance, identical <page>number</page>s, <page>string</page>s and <page>boolean</page>s are always considered as the same. But <page>function</page>s, <page>table</page>s, <page>Vector</page>s, <page>Angle</page>s and **NULL** <page>Entity</page>s can seem identical while actually being distinct in memory.
```
local function1 = function()
print( "hello world" )
end
local function2 = function()
print( "hello world" )
end
print( function1 )
print( function2 )
--[[
Output:
function: 0x30afbb60
function: 0x30afde20
]]
```