Garry's Mod Wiki

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⤶ ]]⤶ ```⤶ ⤶ ⤶ ⤶ ⤶