Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

How to randomly list objects of an array?

To put it simple, I want to go through every single object in an array in a way that is random every time.

I've found out that by using this method:

local array = something:GetChildren()

for i=1, #array do
	array[i] --do something
end

it would go through every single object of the array by their time of creation which would be the same every time the code is run. So, I'm asking you what would be the easiest way to make it happen randomly.

So far I've thought of

local i = math.random(1,#array)

but it would be too messy, because you would have to keep track of objects that' already have gone through.

Thankyou in advance!

1 comment

0

This is a good question. Upvote this, people.

4 answers

✓ Accepted answer

There are evidently many solutions to this problem, but I'll just share the first (and in my opinion, the simplest) that comes to mind.

Solution

You most likely don't want to change the state of the array whilst listing random indices, so you probably want some sort of buffer to temporarily represent the array. Then, a new table to store the new random configuration of the array.

local function randomSortArray(array)
	-- A buffer that is a direct clone of the array for temporary use
	local buffer = {}
	for i = 1, #array do
		buffer[i] = array[i]
	end

	-- The new array that represents a random configuration of the buffer array
	local newArray = {}
	while (#buffer > 0) do
		-- Remove the randomly chosen index from the buffer to ensure original values each iteration
		newArray[#newArray+1] = table.remove(buffer, math.random(#buffer))
	end

	-- Teturn the random configuration
	return newArray
end

Here's a Walkthrough

  • Create a clone of the original array (a buffer)

    • buffer = {}
  • Copy all elements from array into the buffer

    • for i = 1, #array do buffer[i] = array[i]; end
  • Create a new empty table to store the random order of the array

    • newArray = {}
  • Traverse the buffer, choose a random index, and insert that into the new array

    • newArray[#newArray+1] = table.remove(buffer, math.random(#buffer))
  • Return the new array

Notice that, while inserting the next random index to the new array, we're also removing it from the buffer. This is so that we update the probability according to the size of the array, while retaining it's array structure. I hope this helped. If you have any questions, just leave a comment and I'll get to it.

4 comments

0

You're a genius. I plan on using this code

0

I am not sure why you are using pairs in the second loop?

0

Haha, good point kingdom5. I'll have to elaborate on why I did that later. For now, I've updated it with something else that I'll also try to explain.

0

THANKYOU A LOT for your very clever solution!

I would use a temporary permutation array:

local function RandomPermutation(n)
    -- Initialize the permutation as the array 1...n:
    local perm = {}
    for i = 1, n do
        perm[i] = i
    end

    -- A Fisher-Yates shuffle:
    for i = 1, n-1 do
        local j = math.random(i, n)
        perm[i], perm[j] = perm[j], perm[i]
    end

    return perm
end

-- Example:
local array = something:GetChildren()
local perm = RandomPermutation(#array)
for _, i in ipairs(perm) do
    DoSomething(array[i])
end

You might possibly also do a random shuffle directly reordering the array you want to loop over, instead.

5 comments

0

for _, i :thonk:

0

@incapaxian no?

0

with generic for loops, it is convention to name your index and value variables 'i' and 'v' where i is the index, whilst v is the value. I'm just saying that little portion is misleading.

0

it's also convention to name dummy values underscores. It's not misleading; it just leads you to believe that that value is irrelevant in the context of the situation.

0

@RayCurse, I believe they mean it should be _, v rather than _, i, since the second variable is a value not an index.

I copied this from the dev forum, but I think you could go:

local array = {"I", "am", "kazeks123", "Me", "is", "cool"}
local function Shuffle(t)
	for i = #t, 2, -1 do
		local j = Random.new():NextInteger(1, i)
		t[j], t[i] = t[i], t[j]
	end
end
Shuffle(array)
for i, v in pairs(array) do
	print(v)
end

(if you want to keep the original table maybe make the shuffled table a duplicate of it)

Hope this helps

Don't re-invent the wheel -- shuffling a list is a common problem. It's a little surprising Lua doesn't have an in-built solution. Then again, there are many things Lua doesn't have because it's not a very good language.

From here: https://gist.github.com/Uradamus/10323382

function shuffle(tbl)
  for i = #tbl, 2, -1 do
    local j = math.random(i)
    tbl[i], tbl[j] = tbl[j], tbl[i]
  end
  return tbl
end

This should shuffle a table in-place. Copy the table before passing it if you need to retain order.

function copyTable(tbl)
  newTable = {}
  for i, v in pairs(tbl) do
    newTable[i] = v
  end
  return newTable
end

#edit 1

smh "don't re-invent the wheel" then does that

function shuffleButNotInPlace(oldTable)
  tbl = {unpack(oldTable)}
  for i = #tbl, 2, -1 do
    local j = math.random(i)
    tbl[i], tbl[j] = tbl[j], tbl[i]
  end
  return tbl
end

3 comments

1

You can just do tbl2 = {unpack(tbl1)} if we are talking array. But your method can also work for dictionaries

0

@ incapaxian dictionaries have no order and cannot be made random

0

I understand that dictionaries do not impose an order on their values whilst arrays do. But this is talking about arrays, and the :GetChildren() function, returns an array

This discussion is preserved as it was. New answers, comments, and votes are closed.