Revision Difference
Tables:_Bad_Habits#511800
<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⤶
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⤶
]]⤶
```⤶
⤶
⤶
⤶
⤶