Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to transpose an array more Swiftly?

I want to transpose an array of arrays like this:

Original array:

[
    [1,2,3],
    [4,5,6],
    [7,8,9]
]

Result:

[
    1,4,7,
    2,5,8,
    3,6,9
]
[1,4,7,2,5,8,3,6,9]

Assume all the subarrays have the same length.

If you haven't noticed already, the first three items in the result is the first item of the three subarrays. The fourth, fifth and sixth items in the result is the second item of each subarray.

If you still don't understand, maybe this will help:

At the moment, I have this:

func flatten(array: [[Int]]) -> [Int] {
    var flat = [Int]()
    for i in 0..<array[0].count {
        for subarray in array {
            flat.append(subarray[i])
        }
    }
    return flat
}

I don't think that is very swfity. How can I do this in a swifty way?

Just to avoid being an XY problem, here's why I want to do this.

I am developing a board game. I am using HLGridNode (It's basically a bunch of squares in a grid-like layout) from HLSpriteKit as the board game's board. To edit the contents of the grid node, I need to pass in an 1D array of sprite nodes, not a 2D array.

To make my life easier, I stored the model objects in a 2D array. This way, I can refer to the sqaure 5 squares from the left and 2 squares from the top just by doing:

modelObjects[5][2]

If I flatten the 2D array using .flatMap { $0 } and pass the result to the grid node, modelObjects[5][2] would appear to be 2 squares from the left and 5 squares from the top.

This is not a duplicate of this because that question seems to have definite number of arrays to work with. Although I can put my 2D array into a loop, and do those enumerate().map {...} stuff, it seems like a really long-winded approach. I think there must be a simpler to do this with 2D arrays.

like image 577
Sweeper Avatar asked Oct 06 '16 07:10

Sweeper


People also ask

How do you transpose a matrix algorithm?

Transpose of a matrix is obtained by changing rows to columns and columns to rows. In other words, transpose of A[N][M] is obtained by changing A[i][j] to A[j][i].

How do you transpose a matrix in python?

NumPy Matrix transpose() - Transpose of an Array in Python The transpose of a matrix is obtained by moving the rows data to the column and columns data to the rows. If we have an array of shape (X, Y) then the transpose of the array will have the shape (Y, X).


2 Answers

Here's an improvement on Shadow Of's answer:

extension Collection where Self.Iterator.Element: RandomAccessCollection {
    // PRECONDITION: `self` must be rectangular, i.e. every row has equal size.
    func transposed() -> [[Self.Iterator.Element.Iterator.Element]] {
        guard let firstRow = self.first else { return [] }
        return firstRow.indices.map { index in
            self.map{ $0[index] }
        }
    }
}

let matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
]
matrix.transposed().forEach{ print($0) }
like image 160
Alexander Avatar answered Nov 15 '22 10:11

Alexander


You can receive result you wanted by transpose your 2d matrix, using, for example, this function:

func matrixTranspose<T>(_ matrix: [[T]]) -> [[T]] {
    if matrix.isEmpty {return matrix}
    var result = [[T]]()
    for index in 0..<matrix.first!.count {
        result.append(matrix.map{$0[index]})
    }
    return result
}

and applying flatten (joined in swift 3) then.

let arr = [[1,2,3],[4,5,6],[7,8,9]]
print(matrixTranspose(arr))
// [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

print(matrixTranspose(arr).flatMap{$0})
// [1, 4, 7, 2, 5, 8, 3, 6, 9]

Extension version:

extension Collection where Self.Iterator.Element: Collection {
    var transpose: Array<Array<Self.Iterator.Element.Iterator.Element>> {
        var result = Array<Array<Self.Iterator.Element.Iterator.Element>>()
        if self.isEmpty {return result}

        var index = self.first!.startIndex
        while index != self.first!.endIndex {
            var subresult = Array<Self.Iterator.Element.Iterator.Element>()
            for subarray in self {
                subresult.append(subarray[index])
            }
            result.append(subresult)
            index = self.first!.index(after: index)
        }
        return result
    }
}

with usage

let arr = [[1,2,3],[4,5,6],[7,8,9]]
print(arr.transpose)
// [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
like image 45
Shadow Of Avatar answered Nov 15 '22 11:11

Shadow Of