I've got an array of arrays, something like:
[
[1,2,3],
[1,2,3],
[1,2,3],
]
I would like to transpose it to get the following array:
[
[1,1,1],
[2,2,2],
[3,3,3],
]
It's not difficult to programmatically do so using loops:
function transposeArray(array, arrayLength){
var newArray = [];
for(var i = 0; i < array.length; i++){
newArray.push([]);
};
for(var i = 0; i < array.length; i++){
for(var j = 0; j < arrayLength; j++){
newArray[j].push(array[i][j]);
};
};
return newArray;
}
This, however, seems bulky, and I feel like there should be an easier way to do it. Is there?
This question is related to
javascript
arrays
matrix
transpose
Just another variation using Array.map
. Using indexes allows to transpose matrices where M != N
:
// Get just the first row to iterate columns first
var t = matrix[0].map(function (col, c) {
// For each column, iterate all rows
return matrix.map(function (row, r) {
return matrix[r][c];
});
});
All there is to transposing is mapping the elements column-first, and then by row.
I think this is slightly more readable. It uses Array.from
and logic is identical to using nested loops:
var arr = [_x000D_
[1, 2, 3, 4],_x000D_
[1, 2, 3, 4],_x000D_
[1, 2, 3, 4]_x000D_
];_x000D_
_x000D_
/*_x000D_
* arr[0].length = 4 = number of result rows_x000D_
* arr.length = 3 = number of result cols_x000D_
*/_x000D_
_x000D_
var result = Array.from({ length: arr[0].length }, function(x, row) {_x000D_
return Array.from({ length: arr.length }, function(x, col) {_x000D_
return arr[col][row];_x000D_
});_x000D_
});_x000D_
_x000D_
console.log(result);
_x000D_
If you are dealing with arrays of unequal length you need to replace arr[0].length
with something else:
var arr = [_x000D_
[1, 2],_x000D_
[1, 2, 3],_x000D_
[1, 2, 3, 4]_x000D_
];_x000D_
_x000D_
/*_x000D_
* arr[0].length = 4 = number of result rows_x000D_
* arr.length = 3 = number of result cols_x000D_
*/_x000D_
_x000D_
var result = Array.from({ length: arr.reduce(function(max, item) { return item.length > max ? item.length : max; }, 0) }, function(x, row) {_x000D_
return Array.from({ length: arr.length }, function(x, col) {_x000D_
return arr[col][row];_x000D_
});_x000D_
});_x000D_
_x000D_
console.log(result);
_x000D_
You can do it in in-place by doing only one pass:
function transpose(arr,arrLen) {
for (var i = 0; i < arrLen; i++) {
for (var j = 0; j <i; j++) {
//swap element[i,j] and element[j,i]
var temp = arr[i][j];
arr[i][j] = arr[j][i];
arr[j][i] = temp;
}
}
}
A library-free implementation in TypeScript that works for any matrix shape that won't truncate your arrays:
const rotate2dArray = <T>(array2d: T[][]) => {
const rotated2d: T[][] = []
return array2d.reduce((acc, array1d, index2d) => {
array1d.forEach((value, index1d) => {
if (!acc[index1d]) acc[index1d] = []
acc[index1d][index2d] = value
})
return acc
}, rotated2d)
}
If you have an option of using Ramda JS and ES6 syntax, then here's another way to do it:
const transpose = a => R.map(c => R.map(r => r[c], a), R.keys(a[0]));_x000D_
_x000D_
console.log(transpose([_x000D_
[1, 2, 3, 4],_x000D_
[5, 6, 7, 8],_x000D_
[9, 10, 11, 12]_x000D_
])); // => [[1,5,9],[2,6,10],[3,7,11],[4,8,12]]
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
_x000D_
Many good answers here! I consolidated them into one answer and updated some of the code for a more modern syntax:
One-liners inspired by Fawad Ghafoor and Óscar Gómez Alcañiz
function transpose(matrix) {
return matrix[0].map((col, i) => matrix.map(row => row[i]));
}
function transpose(matrix) {
return matrix[0].map((col, c) => matrix.map((row, r) => matrix[r][c]));
}
Functional approach style with reduce by Andrew Tatomyr
function transpose(matrix) {
return matrix.reduce((prev, next) => next.map((item, i) =>
(prev[i] || []).concat(next[i])
), []);
}
Lodash/Underscore by marcel
function tranpose(matrix) {
return _.zip(...matrix);
}
// Without spread operator.
function transpose(matrix) {
return _.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
}
Vanilla approach
function transpose(matrix) {
const rows = matrix.length, cols = matrix[0].length;
const grid = [];
for (let j = 0; j < cols; j++) {
grid[j] = Array(rows);
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
grid[j][i] = matrix[i][j];
}
}
return grid;
}
Vanilla in-place ES6 approach inspired by Emanuel Saringan
function transpose(matrix) {
for (var i = 0; i < matrix.length; i++) {
for (var j = 0; j < i; j++) {
const temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
}
// Using destructing
function transpose(matrix) {
for (var i = 0; i < matrix.length; i++) {
for (var j = 0; j < i; j++) {
[matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
}
}
}
You can achieve this without loops by using the following.
It looks very elegant and it does not require any dependencies such as jQuery of Underscore.js.
function transpose(matrix) {
return zeroFill(getMatrixWidth(matrix)).map(function(r, i) {
return zeroFill(matrix.length).map(function(c, j) {
return matrix[j][i];
});
});
}
function getMatrixWidth(matrix) {
return matrix.reduce(function (result, row) {
return Math.max(result, row.length);
}, 0);
}
function zeroFill(n) {
return new Array(n+1).join('0').split('').map(Number);
}
Minified
function transpose(m){return zeroFill(m.reduce(function(m,r){return Math.max(m,r.length)},0)).map(function(r,i){return zeroFill(m.length).map(function(c,j){return m[j][i]})})}function zeroFill(n){return new Array(n+1).join("0").split("").map(Number)}
Here is a demo I threw together. Notice the lack of loops :-)
// Create a 5 row, by 9 column matrix._x000D_
var m = CoordinateMatrix(5, 9);_x000D_
_x000D_
// Make the matrix an irregular shape._x000D_
m[2] = m[2].slice(0, 5);_x000D_
m[4].pop();_x000D_
_x000D_
// Transpose and print the matrix._x000D_
println(formatMatrix(transpose(m)));_x000D_
_x000D_
function Matrix(rows, cols, defaultVal) {_x000D_
return AbstractMatrix(rows, cols, function(r, i) {_x000D_
return arrayFill(cols, defaultVal);_x000D_
});_x000D_
}_x000D_
function ZeroMatrix(rows, cols) {_x000D_
return AbstractMatrix(rows, cols, function(r, i) {_x000D_
return zeroFill(cols);_x000D_
});_x000D_
}_x000D_
function CoordinateMatrix(rows, cols) {_x000D_
return AbstractMatrix(rows, cols, function(r, i) {_x000D_
return zeroFill(cols).map(function(c, j) {_x000D_
return [i, j];_x000D_
});_x000D_
});_x000D_
}_x000D_
function AbstractMatrix(rows, cols, rowFn) {_x000D_
return zeroFill(rows).map(function(r, i) {_x000D_
return rowFn(r, i);_x000D_
});_x000D_
}_x000D_
/** Matrix functions. */_x000D_
function formatMatrix(matrix) {_x000D_
return matrix.reduce(function (result, row) {_x000D_
return result + row.join('\t') + '\n';_x000D_
}, '');_x000D_
}_x000D_
function copy(matrix) { _x000D_
return zeroFill(matrix.length).map(function(r, i) {_x000D_
return zeroFill(getMatrixWidth(matrix)).map(function(c, j) {_x000D_
return matrix[i][j];_x000D_
});_x000D_
});_x000D_
}_x000D_
function transpose(matrix) { _x000D_
return zeroFill(getMatrixWidth(matrix)).map(function(r, i) {_x000D_
return zeroFill(matrix.length).map(function(c, j) {_x000D_
return matrix[j][i];_x000D_
});_x000D_
});_x000D_
}_x000D_
function getMatrixWidth(matrix) {_x000D_
return matrix.reduce(function (result, row) {_x000D_
return Math.max(result, row.length);_x000D_
}, 0);_x000D_
}_x000D_
/** Array fill functions. */_x000D_
function zeroFill(n) {_x000D_
return new Array(n+1).join('0').split('').map(Number);_x000D_
}_x000D_
function arrayFill(n, defaultValue) {_x000D_
return zeroFill(n).map(function(value) {_x000D_
return defaultValue || value;_x000D_
});_x000D_
}_x000D_
/** Print functions. */_x000D_
function print(str) {_x000D_
str = Array.isArray(str) ? str.join(' ') : str;_x000D_
return document.getElementById('out').innerHTML += str || '';_x000D_
}_x000D_
function println(str) {_x000D_
print.call(null, [].slice.call(arguments, 0).concat(['<br />']));_x000D_
}
_x000D_
#out {_x000D_
white-space: pre;_x000D_
}
_x000D_
<div id="out"></div>
_x000D_
I found the above answers either hard to read or too verbose, so I write one myself. And I think this is most intuitive way to implement transpose in linear algebra, you don't do value exchange, but just insert each element into the right place in the new matrix:
function transpose(matrix) {
const rows = matrix.length
const cols = matrix[0].length
let grid = []
for (let col = 0; col < cols; col++) {
grid[col] = []
}
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
grid[col][row] = matrix[row][col]
}
}
return grid
}
You could use underscore.js
_.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
shortest way with lodash
/underscore
and es6
:
_.zip(...matrix)
where matrix
could be:
const matrix = [[1,2,3], [1,2,3], [1,2,3]];
function invertArray(array,arrayWidth,arrayHeight) {
var newArray = [];
for (x=0;x<arrayWidth;x++) {
newArray[x] = [];
for (y=0;y<arrayHeight;y++) {
newArray[x][y] = array[y][x];
}
}
return newArray;
}
reverseValues(values) {
let maxLength = values.reduce((acc, val) => Math.max(val.length, acc), 0);
return [...Array(maxLength)].map((val, index) => values.map((v) => v[index]));
}
One-liner that does not change given array.
a[0].map((col, i) => a.map(([...row]) => row[i]))
I didn't find an answer that satisfied me, so I wrote one myself, I think it is easy to understand and implement and suitable for all situations.
transposeArray: function (mat) {
let newMat = [];
for (let j = 0; j < mat[0].length; j++) { // j are columns
let temp = [];
for (let i = 0; i < mat.length; i++) { // i are rows
temp.push(mat[i][j]); // so temp will be the j(th) column in mat
}
newMat.push(temp); // then just push every column in newMat
}
return newMat;
}
const transpose = array => array[0].map((r, i) => array.map(c => c[i]));_x000D_
console.log(transpose([[2, 3, 4], [5, 6, 7]]));
_x000D_
here is my implementation in modern browser (without dependency):
transpose = m => m[0].map((x,i) => m.map(x => x[i]))
Another approach by iterating the array from outside to inside and reduce the matrix by mapping inner values.
const_x000D_
transpose = array => array.reduce((r, a) => a.map((v, i) => [...(r[i] || []), v]), []),_x000D_
matrix = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];_x000D_
_x000D_
console.log(transpose(matrix));
_x000D_
Edit: This answer would not transpose the matrix, but rotate it. I didn't read the question carefully in the first place :D
clockwise and counterclockwise rotation:
function rotateCounterClockwise(a){
var n=a.length;
for (var i=0; i<n/2; i++) {
for (var j=i; j<n-i-1; j++) {
var tmp=a[i][j];
a[i][j]=a[j][n-i-1];
a[j][n-i-1]=a[n-i-1][n-j-1];
a[n-i-1][n-j-1]=a[n-j-1][i];
a[n-j-1][i]=tmp;
}
}
return a;
}
function rotateClockwise(a) {
var n=a.length;
for (var i=0; i<n/2; i++) {
for (var j=i; j<n-i-1; j++) {
var tmp=a[i][j];
a[i][j]=a[n-j-1][i];
a[n-j-1][i]=a[n-i-1][n-j-1];
a[n-i-1][n-j-1]=a[j][n-i-1];
a[j][n-i-1]=tmp;
}
}
return a;
}
If using RamdaJS is an option, this can be achieved in one line: R.transpose(myArray)
ES6 1liners as :
let invert = a => a[0].map((col, c) => a.map((row, r) => a[r][c]))
so same as Óscar's, but as would you rather rotate it clockwise :
let rotate = a => a[0].map((col, c) => a.map((row, r) => a[r][c]).reverse())
Neat and pure:
[[0, 1], [2, 3], [4, 5]].reduce((prev, next) => next.map((item, i) =>
(prev[i] || []).concat(next[i])
), []); // [[0, 2, 4], [1, 3, 5]]
Previous solutions may lead to failure in case an empty array is provided.
Here it is as a function:
function transpose(array) {
return array.reduce((prev, next) => next.map((item, i) =>
(prev[i] || []).concat(next[i])
), []);
}
console.log(transpose([[0, 1], [2, 3], [4, 5]]));
Update. It can be written even better with spread operator:
const transpose = matrix => matrix.reduce(
($, row) => row.map((_, i) => [...($[i] || []), row[i]]),
[]
)
Since nobody so far mentioned a functional recursive approach here is my take. An adaptation of Haskell's Data.List.transpose
.
var transpose = as => as.length ? as[0].length ? [as.reduce((rs, a) => a.length ? (rs.push(a[0]), rs) :
rs, []
), ...transpose(as.map(a => a.slice(1)))] :
transpose(as.slice(1)) :
[],
mtx = [
[1],
[1, 2],
[1, 2, 3]
];
console.log(transpose(mtx))
_x000D_
.as-console-wrapper {
max-height: 100% !important
}
_x000D_
Source: Stackoverflow.com