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
5 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:

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!

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

4 answers

Log in to vote
5
Answered by 5 years ago
Edited 5 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.

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.

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

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.

0
`for _, i` :thonk: User#24403 69 — 5y
0
@incapaxian no? aschepler 135 — 5y
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 — 5y
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 — 5y
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 — 5y
Log in to vote
0
Answered by 5 years ago
Edited 5 years ago

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

Log in to vote
0
Answered by
fredfishy 833 Moderation Voter
5 years ago
Edited 5 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

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
1
You can just do `tbl2 = {unpack(tbl1)}` if we are talking array. But your method can also work for dictionaries User#24403 69 — 5y
0
@ incapaxian dictionaries have no order and cannot be made random User#5423 17 — 5y
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 — 5y

Answer this question