How to pass arguments to a function based on variable names?

Hello developers,

Sorry if you thought this topic’s title is difficult to understand.

To put it simply, I want to make a code that passes specific arguments by their variable name.

Example code:

local ArgumentTable = {First, Second, Third}

local function SumNumbers(...: number): number
	local Numbers = {...}
	local TotalNumber = 0
	for _, Number in ipairs(Numbers) do
		TotalNumber += Number
	end
	return TotalNumber
end

local First = 5
local Second = 5
local Third = 5

SumNumbers(...) -- Send arguments that are in "ArgumentTable".

Or, if you have a idea more greater than it, please share it!

Any help would be appreciated!

Correct me if I understand this wrong, but if you are trying to pass through a table you can simply do that by setting that as a parameter. just do SumNumbers(ArgumentTable)

1 Like

I don’t believe that you can (at least, not in that way).

If you utilise a dictionary, you could potentially get the result you want.
e.g.

ArgumentTable = {"First", "Second", "Third"}
local function DictToParams(args, argTable)
	local Result = {}	
	for _ t in argTable do
		table.insert(Result, args[t])
	end
	return table.unpack(Result)
end

local function SumNumbers(...: number): number
	local Numbers = {...}
	local TotalNumber = 0
	for _, Number in ipairs(Numbers) do
		TotalNumber += Number
	end
	return TotalNumber
end
local args = {
	First = 5,
	Second = 5,
	Third = 5
}

SumNumbers(DictToParams(args, ArgumentTable)) -- Send arguments that are in "ArgumentTable".

(Code untested. if it errors, lmk)


@Fietowski, your method would cause the values to be out of date (if you use a variable to set a value in a table, then change the variable and review the table, the table is unchanged.)

Yeah i understand what he meant now, dont mind my previous suggestion

Is it available to use it without using dictionary? :thinking:

Because, in real code, I defined variables just using local.

And I think wrapping them as dictionary is inefficient.

The dictionary method is the closest to getting what you want, from what I can tell.
Otherwise, you’ll just to need manually provide the arguments to the function

Then, what do you think what is the most efficient way to pass the arguments without using my way?

Either just putting them in manually

SumNumbers(First, Second, Third)

or use the dictionary method I said above

Why do you specifically need it this way? Seems kind of odd and looks like you might’ve been overcomplicating the approach you use. Is there any real use case you can show?

If you need to sum a table of numbers, why not just adding numbers directly to an array, using table.insert? And then passing the array as an argument.

The code you provided is basically equivalent to:

local function SumNumbers(Numbers: {number}): number
	local TotalNumber = 0
	for _, Number in ipairs(Numbers) do
		TotalNumber += Number
	end
	return TotalNumber
end

Well, to explain more detail, my code do place specific objects and give them their own function. But, each function receives different arguments. So I want to set their own arguments for each and make the list using a table.

Yeah, but that was just for an example though.

In that case using varargs is justified, but you can still pass the arguments you need within the function via a table as an alternate.

Even if you’ll use vararg approach you still know what kind of arguments it will contain and index them thru something like:

local Char = {...}[1]

So my point is why not passing arguments thru a table with more readable indexes?

Could you please provide example code to understand more well?

Here, some pseudo-code:

Vararg:

local function printCharName(...)
    local args = {...}
    local Char = args[1]
    print(`Character name is {Char.Name}`
end

printCharName(game.Players.LocalPlayer.Character) -- prints ur char name

Arg table:

local function printCharName(args)
    local Char = args.Char
    print(`Character name is {Char.Name}`
end

printCharName({Char = game.Players.LocalPlayer.Character}) -- prints ur char name

Basically the same result. Varargs should be used only when you don’t know any of the arguments, or you can use vararg as an extension to known arguments, like:

local function applyCharStats(Char: Model, ...)
    local extraArgs = {...}
    print(`Character name is {Char.Name}`)

    for _, Data in extraArgs do
        if typeof(Data) == "table" then
            if Data.Speed then
                -- apply some calculations etc
            end
            if Data.Damage then
                -- apply some calculations etc
            end
            if Data.Trait then
               -- apply trait
            end
        end
    end
end

applyCharStats(game.Players.LocalPlayer.Character, {Speed = 10, Damage = 20}, {Trait = "Brave"}) 

That’s if we don’t specifically know arguments - we had to parse them and search.

But we can follow a different approach, when we specifically know what to do:

-- we can use a switch case for diff stats

local StatCallbacks = {
    Speed = function(Char: Model, Value: Number)
        -- do some calcs and apply
    end,
    Damage = function(Char: Model, Value: Number)
        -- do some calcs and apply
    end,
    Trait = function(Char: Model, Name: string)
        -- apply trait
    end,
}

local function applyCharStats(Char: Model, Config)
    print(`Character name is {Char.Name}`)

    for Stat, Value in Config do
        if StatCallbacks[Stat] then
            StatCallbacks[Stat](Char, Value)
        end
    end
end

applyCharStats(game.Players.LocalPlayer.Character, {Speed = 10, Damage = 20, Trait = "Brave"}) 

As you can see, we’ve achieved the same goal without varargs and improved the code as well.

Thank you for replying. I think that’s a great way, but it’s not suitable for my code. I will use really irregular arguments for each and it can be unexpected. I will provide you a code:

local function Spawn(Object: BasePart, CloneRoom: Model): ()
	local Template = ObjectsTemplates:FindFirstChild(string.split(Object.Name, "Spawn")[1]) :: Model
	local CloneTemplate = Template:Clone()
	CloneTemplate:PivotTo(Object.CFrame)
	CloneTemplate.Parent = CloneRoom
	local ObjectName = Roles[Object.Name]
	local ObjectModuleScript = ObjectsController:FindFirstChild(ObjectName) :: Instance
	if not ObjectModuleScript then
		error(script.Name..": "..ObjectName.." module not found!")
	end
	local ObjectModule = require(ObjectModuleScript) :: ObjectModule
	local NewObjectThread = coroutine.create(function()
		ObjectModule.new(...) -- This line is what I mentioned.
	end)
	local RunningObjectFunction = RunningObjectFunctions[CloneRoom]
	if not RunningObjectFunction then
		RunningObjectFunctions[CloneRoom] = {}
	end
	coroutine.resume(NewObjectThread)
	table.insert(RunningObjectFunctions[CloneRoom], {CloneTemplate, NewObjectThread} :: {Model | thread})
end

I’m sorry if I have misunderstood your reply.