How to shuffle multidimensional array?

Viewed 519

I am trying to randomly shuffle a 2D array. how can I do it? I know only the way to shuffle one array with method shuffled. Should I unit all 2d array into one big array and shuffle and make it a 2d array or there is a better approach?

[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
4 Answers

You are right that a good way to approach shuffling a 2-D array is to flatten it and shuffle the values. The tricky part is how to get the shuffled values back into the original structure of the array.

By making the shuffled array into an iterator iter, we can call iter.next() to get each value and use nested maps to access and replace the original values:

var arr = [[1], [2, 3], [4, 5, 6]]

var iter = arr.joined().shuffled().makeIterator()

let arr2 = arr.map { $0.map { _ in iter.next()! } }

print(arr2)
[[4], [5, 1], [3, 6, 2]]

Make it into a Generic Function: func shuffle2D<T>(_ arr: [[T]]) -> [[T]]

We can turn this into a generic function which can shuffle any 2D array:

func shuffle2D<T>(_ arr: [[T]]) -> [[T]] {
    var iter = arr.joined().shuffled().makeIterator()

    return arr.map { $0.compactMap { _ in iter.next() }}
}

Note: I have changed the internal map into a compactMap so that we can avoid force unwrapping iter.next().

Examples:

print(shuffle2D([[1, 2, 3], [4, 5, 6]]))
[[2, 5, 6], [3, 1, 4]]
print(shuffle2D([["a", "b"], ["c", "d"], ["e", "f"]]))
[["e", "a"], ["b", "d"], ["f", "c"]]
let array = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
             [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
             [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
             [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
             [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
             [2, 2, 2, 2, 2, 0, 0, 0, 0, 0],
             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

let shuffled = shuffle2D(array)
print(shuffled)
[[0, 1, 0, 1, 1, 0, 1, 1, 0, 0], [0, 1, 0, 0, 1, 1, 1, 2, 0, 1], [0, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 0, 1, 0, 2, 1, 2, 1, 0, 1], [1, 0, 0, 0, 2, 1, 1, 0, 1, 1], [1, 1, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 1, 1, 2, 0, 0, 1, 1, 0], [1, 0, 1, 1, 0, 0, 0, 1, 1, 0], [0, 0, 0, 1, 1, 0, 1, 1, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0, 0, 0]]

You could iterate over the outer array with a for loop and shuffle each array you iterate over via the shuffle method. Here's my idea in pseudocode:

for inner_array in outer_array:
    shuffle(inner_array)

Edit

As far as I understand your question now, I would try to add the existing array to a new array in a not-so-random order, like this:

new_array = []
for i=0; i < oldarray.length(); i++:
    new_array.append(oldarray[randomNumber])

You would have to look out for possible duplicates (i.e. by removing the array in the for loop from the old array) and of cause that your randomNumber is generated between the borders of the oldarray.

You can use the first answer and the second answer together actually depending on your needs.

First of all get a single combined shuffled array from the multidimensional array, i.e.

let arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
let shuffled = arr.joined().shuffled() //[1, 2, 5, 6, 11, 9, 4, 7, 12, 3, 8, 10]

Create an extension that divides a single array into chunks, i.e.

extension Array {
    func chunks(of size: Int) -> [[Element]] {
        return stride(from: 0, to: self.count, by: size).map {
            Array(self[$0 ..< Swift.min($0 + size, count)])
        }
    }
}

Usage:

let result = shuffled.chunks(of: 3) [[3, 1, 2], [5, 6, 4], [7, 8, 9], [11, 10, 12]]

You can always do it like this:

var matrix = [[1, 3, 2], [7, 1, 2], [9, 4, 1]]
var shuffled = matrix.map({ $0.shuffled() })

print(shuffled)

One of the possible outputs being this

[[3, 2, 1], [2, 7, 1], [9, 4, 1]]

Edit:

If the whole matrix is to be shuffled though, you should probably first flatMap the matrix, shuffle the array, and then chunk the array back into a matrix.

If you are concerned about performance, You should then do some well known array shuffling algorithm (Knuth shuffle) , but with the matrix intact. Instead, use a linearized matrix variant (where you convert a matrix position (x, y) to a vector position (y * (x - 1)) + y)

Related