Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
6

How to randomly list objects of an array?

Asked by
kazeks123 195
6 years ago

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:

1local array = something:GetChildren()
2 
3for i=1, #array do
4    array[i] --do something
5end

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

1local 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!

0
This is a good question. Upvote this, people. User#24403 69 — 6y

4 answers

Log in to vote
5
Answered by 6 years ago
Edited 6 years ago

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.

01local function randomSortArray(array)
02    -- A buffer that is a direct clone of the array for temporary use
03    local buffer = {}
04    for i = 1, #array do
05        buffer[i] = array[i]
06    end
07 
08    -- The new array that represents a random configuration of the buffer array
09    local newArray = {}
10    while (#buffer > 0) do
11        -- Remove the randomly chosen index from the buffer to ensure original values each iteration
12        newArray[#newArray+1] = table.remove(buffer, math.random(#buffer))
13    end
14 
15    -- Teturn the random configuration
16    return newArray
17end

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.

0
You're a genius. I plan on using this code User#24403 69 — 6y
0
I am not sure why you are using pairs in the second loop? User#5423 17 — 6y
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. ScriptGuider 5640 — 6y
0
THANKYOU A LOT for your very clever solution! kazeks123 195 — 6y
Ad
Log in to vote
1
Answered by
aschepler 135
6 years ago

I would use a temporary permutation array:

01local function RandomPermutation(n)
02    -- Initialize the permutation as the array 1...n:
03    local perm = {}
04    for i = 1, n do
05        perm[i] = i
06    end
07 
08    -- A Fisher-Yates shuffle:
09    for i = 1, n-1 do
10        local j = math.random(i, n)
11        perm[i], perm[j] = perm[j], perm[i]
12    end
13 
14    return perm
15end
View all 22 lines...

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

0
`for _, i` :thonk: User#24403 69 — 6y
0
@incapaxian no? aschepler 135 — 6y
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. User#24403 69 — 6y
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. RayCurse 1518 — 6y
0
@RayCurse, I believe they mean it should be `_, v` rather than `_, i`, since the second variable is a value not an index. fredfishy 833 — 6y
Log in to vote
0
Answered by 6 years ago
Edited 6 years ago

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

01local array = {"I", "am", "kazeks123", "Me", "is", "cool"}
02local function Shuffle(t)
03    for i = #t, 2, -1 do
04        local j = Random.new():NextInteger(1, i)
05        t[j], t[i] = t[i], t[j]
06    end
07end
08Shuffle(array)
09for i, v in pairs(array) do
10    print(v)
11end

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

Hope this helps

Log in to vote
0
Answered by
fredfishy 833 Moderation Voter
6 years ago
Edited 6 years ago

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

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

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

1function copyTable(tbl)
2  newTable = {}
3  for i, v in pairs(tbl) do
4    newTable[i] = v
5  end
6  return newTable
7end

edit 1

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

1function shuffleButNotInPlace(oldTable)
2  tbl = {unpack(oldTable)}
3  for i = #tbl, 2, -1 do
4    local j = math.random(i)
5    tbl[i], tbl[j] = tbl[j], tbl[i]
6  end
7  return tbl
8end
1
You can just do `tbl2 = {unpack(tbl1)}` if we are talking array. But your method can also work for dictionaries User#24403 69 — 6y
0
@ incapaxian dictionaries have no order and cannot be made random User#5423 17 — 6y
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 User#24403 69 — 6y

Answer this question