garrysmod icon indicating copy to clipboard operation
garrysmod copied to clipboard

Add util.In( any thing, vararg values )

Open bydlocodd opened this issue 1 year ago • 8 comments

Allows you to check if thing is among values. Using the function, we can do this:

local myUsergroup = 'user'
if util.In( myUsergroup, 'user', 'vip' ) then
    -- ...
end

... instead of:

local allowedUsergroups = {
    user = true,
    vip = true,
}

local myUsergroup = 'user'
if allowedUsergroups[ myUsergroup ] then
    -- ...
end

... or:

local myUsergroup = 'user'
if myUsergroup == 'user' or myUsergroup == 'vip' then
    -- ...
end

Of course, the second code is faster because it performs a key lookup (which is O(1) vs. my O(n) implementation), but util.In is useful in contexts where performance is not so important

bydlocodd avatar Feb 21 '24 16:02 bydlocodd

Bad naming aside,

local myUsergroup = 'user'
if table.HasValue( { 'user', 'vip' }, myUsergroup ) then
    -- ...
end

I understand this is less efficient than the proposed method, but by your own admission that is not the focus here.

robotboy655 avatar Feb 21 '24 19:02 robotboy655

Not creating the table would indeed be much more efficient when JIT compiles the select calls. Not as efficient as creating a hashtable with true vals, but for dynamic vararg values, this seems like the best solution. Efficiency should be the focus of this addition.

Kefta avatar Feb 21 '24 20:02 Kefta

Not sure how I can improve on my solution ( except for brandonsturgeon's suggestion). Of course, pretty much the same thing can be done using table.HasValue (honestly, I just forgot this function existed), but my method is a bit faster.

Regarding the function name - I was guided by Python syntax:

if thing in (a, b, c):
    # ...

bydlocodd avatar Feb 21 '24 21:02 bydlocodd

Bad naming aside,

local myUsergroup = 'user'
if table.HasValue( { 'user', 'vip' }, myUsergroup ) then
    -- ...
end

I understand this is less efficient than the proposed method, but by your own admission that is not the focus here.

Yes, performance is not a focus, because in that case I will use a key lookup. However, the developed solution, although similar to table.HasValue, is slightly more optimized. In this case, I see my function as a compromise between readability and speed - we sacrifice speed for readability, but not as much as in the case of using table.HasValue

bydlocodd avatar Feb 21 '24 21:02 bydlocodd

With regards to the name, util.InValues?

TankNut avatar Mar 10 '24 19:03 TankNut

util.In:
        sum = 0.286
        avg = 0.00286
        median = 0.0030000000000001
util.In2:
        sum = 0.0020000000000024
        avg = 2.0000000000024e-05
        median = 0
function util.In2( thing, ... )
  local iMax, i = select( '#', ... ), 1

  ::iter::
  if thing == select( i, ... ) then
    return true
  end
  i = i + 1
  if i <= iMax then goto iter end

    return false
end

Heyter avatar Mar 14 '24 12:03 Heyter

util.In:
        sum = 0.286
        avg = 0.00286
        median = 0.0030000000000001
util.In2:
        sum = 0.0020000000000024
        avg = 2.0000000000024e-05
        median = 0
function util.In2( thing, ... )
  local iMax, i = select( '#', ... ), 1

  ::iter::
  if thing == select( i, ... ) then
    return true
  end
  i = i + 1
  if i <= iMax then goto iter end

    return false
end

Sample size and size of ...?

Grocel avatar Mar 14 '24 15:03 Grocel

Sample size and size of ...?

lib: https://github.com/Be1zebub/Small-GLua-Things/blob/master/sh_benchmark.lua

util.In:
        sum = 0.035
        avg = 0.00035
        median = 0
util.In2:
        sum = 0.00099999999999767
        avg = 9.9999999999767e-06
        median = 0
        
	benchmark("util.In", function()
		a = util.In(userName, "admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user",		userName)
	end, 1000, 100)
	benchmark("util.In2", function()
		a = util.In2(userName, "admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "admin", "test", "user", "admin", "test", "user", "admin", "test", "user","admin", "test", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user",  userName)
	end, 1000, 100)

table unpack

util.In:
        sum = 0.113
        avg = 0.00113
        median = 0.0010000000000048
util.In2:
        sum = 6.415
        avg = 0.06415
        median = 0.063000000000017
        
local a = false
local users = {}
local userName = "superadmin"

for i = 1, 253 do
	if i % 2 == 0 then
		users[i] = "admin"
	else
		users[i] = "user"
	end
end

users[254] = 'superadmin'

benchmark("util.In", function()
	a = util.In(userName, unpack(users))
end, 1000, 100)
benchmark("util.In2", function()
	a = util.In2(userName, unpack(users))
end, 1000, 100)

Heyter avatar Mar 14 '24 16:03 Heyter