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.
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].
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).
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) }
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]]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With