Efficient way of getting 3 random items from a table

local GameMaps = {}
	
for _, v in pairs(Maps.Teams:GetChildren()) do
	table.insert(GameMaps, v)
end
	
for _, v in pairs(Maps.FFA:GetChildren()) do
	table.insert(GameMaps, v)
end

local RandomMap1 = GameMaps[math.random(1, #GameMaps)]
local RandomMap2 = GameMaps[math.random(1, #GameMaps)]
local RandomMap3 = GameMaps[math.random(1, #GameMaps)]

This is what I currently got and it’s pretty bad. Main problem is, all 3 maps could technically be the same map, or 2 of the same map, etc. I want all 3 to be different maps. second problem, I don’t know if it’s just me, but going

local RandomMap1 = GameMaps[math.random(1, #GameMaps)]
local RandomMap2 = GameMaps[math.random(1, #GameMaps)]
local RandomMap3 = GameMaps[math.random(1, #GameMaps)]

Just looks bad to me, I don’t know if there’s a cleaner way to do this though. I would have thought changing the 1 to 3 would work, but it still just picks 1 map

print(#GameMaps) -- prints 4
local RandomMap = GameMaps[math.random(3, #GameMaps)]
print(RandomMap) -- Only prints 1 map
6 Likes
local RandomMap = table.remove(GameMaps, math.random(#GameMaps))

table.remove both removes and returns the element from a table.

Please note you’ll need to refill the table when you want it to be able to pick the same elements again (such as when it runs out, or if it’s okay to have the same map in the next round just not in the same round). It’s also probably a good idea to check if the table is size 1, and if so just return the item in position 1 instead of trying to random a number from 1 to 1. This also provides a good time to refresh the table.

2 Likes

You would want to use a function, using variable stores the number making it the same each time

1 Like

You’d need to make a new table with all of the maps and use math.random to get one map, then use table.remove to remove it from the table so that it can not be chosen again. You’d have to repeat this 2 more times to get the 3 maps

Here’s a function that easily does that:

function GetMaps(amt)
    local chosenMaps = {}
    -- Re-adding the values in a new table
    local newTable = {}

    for i,v in pairs(GameMaps) do
        newTable[i] = v
    end
    -- Choosing maps
    for i = 1, amount do
        local r = math.random(1, #newTable)
        chosenMaps[i] = newTable[r]
        table.remove(newTable, r)
    end
   
    return chosenMaps
end
13 Likes

Seems to solve 1 issue. Is there a cleaner way to pick like all 3 random maps without having to do

local RandomMap1 = table.remove(GameMaps, math.random(#GameMaps))
local RandomMap2 = table.remove(GameMaps, math.random(#GameMaps))
local RandomMap3 = table.remove(GameMaps, math.random(#GameMaps))

As for the table being 1, etc. When the maps are being chosen, the table gets preset back to {}

	local GameMaps = {}
	
	for _, v in pairs(Maps.Teams:GetChildren()) do
		table.insert(GameMaps, v)
	end
	
	for _, v in pairs(Maps.FFA:GetChildren()) do
		table.insert(GameMaps, v)
	end
	
	local RandomMap1 = table.remove(GameMaps, math.random(#GameMaps))
	local RandomMap2 = table.remove(GameMaps, math.random(#GameMaps))
	local RandomMap3 = table.remove(GameMaps, math.random(#GameMaps))

	print(RandomMap1, RandomMap2, RandomMap3)
2 Likes

Yes, there’s much cleaner solution to the problem and way more efficient than the other solutions here. Generate a random permutation of numbers from 1 to #GameMaps and then choose the maps as in the example:

function randomPermutation(n)
    assert(n >= 1, "n must be >= 1")
    assert(n == math.floor(n), "n must be an integer")

    local tab = {}
    for i = 1, n do
        tab[i] = i
    end

    for i = 2, n do
        local randomIndex = math.random(i)
        tab[randomIndex], tab[i] = tab[i], tab[randomIndex]
    end

    return tab
end

local permutation = randomPermutation(#GameMaps)
local map1 = GameMaps[permutation[1]]
local map2 = GameMaps[permutation[2]]
local map3 = GameMaps[permutation[3]]

You can shuffle the map list to generate the order first so you don’t need to recreate a list again each time you draw the map.

function ShuffleList()
   for i=1, #GameMaps - 1 do --change it to 3 if you just need to draw 3 maps
      local listI = math.random(i, #GameMaps)
      local temp = GameMaps[i] 
      GameMaps[i] = GameMaps[listI] 
      GameMaps[listI] = temp
   end
end

i typed these in phone, hope it is not wrong :stuck_out_tongue:

4 Likes

Great idea to store the choices at the beginning. I think that this is the most efficient algorithm.

By the way: instead of

local temp = GameMaps[i] 
GameMaps[i] = GameMaps[listI] 
GameMaps[listI] = temp

you can do

GameMaps[i], GameMaps[listI] = GameMaps[listI], GameMaps[i]
1 Like

+1 to this, as it also answers you concern on a cleaner implementation of referencing the maps later.