How do I pick a random item from a dictionary

So basically I need to get a random item from a dictionary. I cant figure it out.
So how do I do it?

local wires = {
	red = {
		wireType = nil
	},
	blue = {
		wireType = nil
	},
	green = {
		wireType = nil
	},
	yellow = {
		wireType = nil
	},
}



local function setupWires()
	-- set defuse wire
end


setupWires()
--make a table
local tabl = {1,2,3,4}

--chose a random value from that table
local randomvalue = tabl[1,math.random(1,#tabl)]

--random value will be between 1 and 4
--[[
having brackets "[]" with a number in them right after a table 
will represent the index or whatever it is called in that table. 
Example: "tabl[2]" is 2

the # just means the amount of items in a table
]]

--in your case it might be something like
local randomwire = wires[1,math.random(1,#wires)]

Tables are different for a dictionary.
They work different from what I know.

are you trying to get a random wire color? I just want to know what your problem here is

The wires are in a dictionary.
Each item in the dictionary is named a certain color.
I need to pick a random one of them.

And this is a dictionary not a table

local function getRandomItem(t)
  local indices = {} -- hold the keys
  for index in t do
    table.insert(indices, index) -- add the element's key to the table
  end
  return t[indices[math.random(#indices)]] -- return a random value
end

I have no idea how this works. Could you explain to me what these things do?
Cause I dont understand this

It loops through the table you send to the function and inserts all the indexes in it, to a separate table. Then, it just gets a random key from the other table and uses it to grab the value from the table you sent to the function.

It sounds complicated, but it’s fairly simple

-- t is the table you want to get a random value of
local indices = {}

for index in t do
  -- loop through the t and get all the indices and insert them into the `indices` list
  table.insert(indices, index)
end

local chosenKey = indices[math.random(#indices)] -- get a random index in the table
return t[chosenKey] -- the same as doing t.index or t["index"]

As an alternative, you could also have another value in each sub-table beside wireType, representing order. Then you would find a random integer between 1 and 4.

local random = Random.new()

local wires = {
	red = {
		wireType = nil,
		wireOrder = 1
	},
	blue = {
		wireType = nil,
		wireOrder = 2
	},
	green = {
		wireType = nil,
		wireOrder = 3
	},
	yellow = {
		wireType = nil,
		wireOrder = 4
	},
}

local function GetRandomWire()
	local randNum = random:NextInteger(1,4)
	
	for wire,info in pairs(wires) do
		if info.wireOrder == randNum then
			return wire
		end
	end
end

local randomWire = GetRandomWire()
print(wires[randomWire].wireOrder)

This also doesn’t randomize the indices each time, if you want to avoid certain wires repeating over time. For example, say you wanted the algorithm to pick anything but yellow, you would would have a repeat loop until random is different than yellow (index 4).

local function GetRandomWire(toExclude)
	local randNum: number
	repeat
		randNum = random:NextInteger(1,4)
	until not toExclude or not table.find(toExclude, randNum)
	
	for wire,info in pairs(wires) do
		if info.wireOrder == randNum then
			return wire
		end
	end
end

-- and
local randomWire = GetRandomWire(table.pack(4))

However, I question whether it’s not more convenient (depending on your specific use case) to organise a table like this:

local wires = {
	{color = "red"; wireType = nil} -- order is 1
	...
}

If I did it like this how do I get it?
Cause I am kinda bad at using tables n stuff

In that case StingyDudeman’s explanation comes in handy.

local random = Random.new()

local wires = {
	{color = "red"; wireType = nil};
	{color = "blue"; wireType = nil};
	{color = "green"; wireType = nil};
	{color = "yellow"; wireType = nil};
}

local function GetRandomWireInfo()
	return wires[random:NextInteger(1,#wires)]
end

local randomWireInfo = GetRandomWireInfo()
print(randomWireInfo.color)

Again, if you wanted to exclude certain numbers, let’s say red and green, it would be even easier then above, but again, it depends on your use case. Ask yourself, is it more valuable for you to have wires accessible by color name, e.g. wires.red, or with numbers, which makes getting random number slightly easier, but wires are accessible by numeric indices, e.g. wires[1] for red info, wires[1].wireType etc.

local function GetRandomWireInfo(toExclude)
	local randNum: number
	repeat
		randNum = random:NextInteger(1,4)
	until not toExclude or not table.find(toExclude, randNum)
	
	return wires[randNum]
end

local randomWireInfo = GetRandomWireInfo(table.pack(1,3,4)) -- always blue

Just to explain this repeat-loop condition:

until not toExclude or not table.find(toExclude, randNum)

When condition is true, loop is going to finish. So if there is no toExlude, then not nil == true. And if there is toExclude, if table.find() would return something, wouldn’t be nil, so not something == nothing. If table.find() doesn’t find the random integer in the table of integers to exclude, it will return nil, and not nil == something, which means our loop can finish.

Happy ending :slight_smile:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.