Hey guys,
so I've been offline for a few months now, mostly to focus on Web-development, and decided to come back and repolish my LUA a bit.
I've followed a few basic tutorials to get back into things, and now tried to make my own leaderboard based on PlayerAdded and Removing events, using Tables.
I have a script, and I know what I'm doing wrong, but I can't figure out a way to fix it, so I'l point out the problem for you.
--VARIABLES folder = script.plrs; --TABLE plrTable = {}; --FUNCTIONS function remove(plrName) if plrName then folder[plrName]:Destroy(); table.remove(plrTable, plrTable[plrName]); --This is obviously the problem, I'm trying to remove a string index from the table, because I don't know the index of the leaving player's name. Is there a way around this? end end function add(plrName) local players = game.Players:GetChildren(); for p = 1, #players do if not folder:FindFirstChild(plrName) then local plr = Instance.new("IntValue"); plr.Name = plrName; plr.Parent = folder; table.insert(plrTable, plrName); end end end --EVENTS game.Players.PlayerAdded:Connect(function(plr) add(plr.Name); end) game.Players.PlayerRemoving:Connect(function(plr) remove(plr.Name); end) --LOOP TABLE VALUES (Just for my own debugging purposes) while wait(1) do print(table.concat(plrTable, ", ")); end
table.remove(table, pos)
removes and returns the element at position pos
, and if there is a gap, shifts elements to the left to fill in that gap, and the length of the table is decreased. If remove
is called without the second argument, by default the last index is removed.
With that in mind, you can use a generic for loop and compare the value of what you want to remove.
-- an example local tbl = {"hello", "world", "foo", "bar", "test"} -- stuff for i, v in ipairs(tbl) do if v == "foo" then local removedValue = table.remove(tbl, i) print(removedValue) --> foo end end print(table.concat(tbl, ", ")) --> hello, world, bar, test -- everything after 'foo' was shifted to the left to fill in the gap created by the removal of 'foo'
Hello there. Your issue is a relatively common one. There are two solutions that I will present to you. A simple solution is to loop through the existing table, find the element and retrieve its index, and finally remove it from the table with table.remove
. There are two problems with this method. One: looping through the table is an O(n) operation, which means that you will, in the worst case, loop through the entire table (now with a list of players, the table might not get that long. However, it is useful to note for future reference). Not only that, but as the page I linked to above states (in reference to table.insert/table.remove when you specify the index it is to remove or add at), "The last two operations are not particularly efficient, as they must move elements up and down." I will demonstrate a simple implementation here:
local function removePlayer(playerName, playerList) for index, playerToCheck in pairs(playerList) do if playerName == playerToCheck then table.remove(playerList, index) end end end
A more efficient method would be to have a table with string indices (the player's name) and access that index directly when you wish to remove from the table. An example implementation:
--VARIABLES folder = script.plrs; --TABLE plrTable = {}; --FUNCTIONS function remove(plrName) if plrName then folder[plrName]:Destroy(); plrTable[plrName] = nil -- this will effectively remove the index from the table end end function add(plrName) local players = game.Players:GetChildren(); for p = 1, #players do if not folder:FindFirstChild(plrName) then local plr = Instance.new("IntValue"); plr.Name = plrName; plr.Parent = folder; plrTable[plrName] = true end end end -- event connections here while true do -- I don't recommend while wait do, more on that in a bit wait(1) local str = "" -- I do this because table.concat does not work on dictionaries for playerName, _ in pairs(plrTable) do str..playerName..", " -- adding to the string of player names end print(str) end
Some of the reasons I don't encourage using while wait() do
can be found here. If you wish, you can read all the way down the thread for further argumentation.
Finally, I would like to address a few logic issues in your code. For some reason, you create a separate function for the player adding and removing events, but you don't use them as the argument for the :Connect()
method. I realize that you can't get the player name directly from the player added/removing events, but just access that property of the player in your function. Here is an example of what I mean:
-- I also recommend the use of get service local players = game:GetService("Players") --VARIABLES folder = script.plrs; --TABLE plrTable = {}; --FUNCTIONS function remove(player) local plrName = player.Name if plrName then folder[plrName]:Destroy(); plrTable[plrName] = nil -- this will effectively remove the index from the table end end function add(player) local plrName = player.Name local playerList = players:GetPlayers() -- get players is the better method, I left this in here to make you aware, but you don't even need playerList. if not folder:FindFirstChild(plrName) then -- I don't know why you loop through the playerList local plr = Instance.new("IntValue"); plr.Name = plrName; plr.Parent = folder; plrTable[plrName] = true end end players.PlayerAdded:Connect(add) -- player argument is passed implicitly to these functions players.PlayerRemoving:Connect(remove) while true do -- I don't recommend while wait do, more on that in a bit wait(1) local str = "" -- I do this because table.concat does not work on dictionaries for playerName, _ in pairs(plrTable) do str..playerName..", " -- adding to the string of player names end print(str) end
I also recommend defining your functions locally, but that is up to you. Example:
local function multiply(a, b) return a*b end