248

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?

Yangshun Tay
  • 49,270
  • 33
  • 114
  • 141
ckersch
  • 7,507
  • 2
  • 37
  • 44
  • 5
    Can you guarantee that the two dimensions will always be the same? 1x1, 2x2, 3x3, etc. What is the `arrayLength` parameter used for exactly? To ensure that you don't go beyond a certain number of elements in the array? – crush Jul 02 '13 at 14:45
  • 9
    This has nothing to do with JQuery, I changed the title. – Joe Jul 02 '13 at 14:47
  • 4
    Check this out: http://stackoverflow.com/questions/4492678/to-swap-rows-with-columns-of-matrix-in-javascript-or-jquery. What you are doing is transposing a matrix – stackErr Jul 02 '13 at 14:48
  • 1
    Yes, transposing. Inverting would be completely different and I'm not interested in it. For now. – ckersch Jul 02 '13 at 14:59
  • 3
    The top left to bottom right diagonal is unchanged so there is an optimisation opportunity. – S Meaden Sep 08 '16 at 17:07
  • `arrayLength` could be replaced with `array[0].length`, no? – James Newton Mar 30 '21 at 22:09
  • Essentially the same as [Javascript equivalent of Python's zip function](/q/4856717/4642212). – Sebastian Simon Feb 06 '22 at 13:08

25 Answers25

318
output = array[0].map((_, colIndex) => array.map(row => row[colIndex]));

map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.

callback is invoked with three arguments: the value of the element, the index of the element, and the Array object being traversed. [source]

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Fawad Ghafoor
  • 6,039
  • 7
  • 41
  • 53
  • 13
    This is a good solution. However, if you care about performance you should use OP's original solution (w/ the bug fix to support M x N arrays where M != N). [check this jsPerf](http://jsperf.com/transpose-2d-array) – Billy McKee Aug 04 '15 at 22:28
  • 6
    If you use it twice on the same array, it comes back to the first one intead of rotating 90' again – Olivier Pons Mar 03 '17 at 13:51
  • This didn't work for me. However, [this did](https://stackoverflow.com/a/13241545/3491991) – zelusp Jul 18 '17 at 20:21
  • 1
    For array = [ [] ]; I would expect transpose() to act as an identity function and return [ [] ]. But instead it returns []. – Alex Aug 21 '17 at 13:40
  • 6
    why `array[0].map` instead of `array.map` ? – John Vandivier Sep 08 '18 at 16:48
  • 9
    `array[0].map` because he wants to iterate however many times that there are columns, `array.map` would iterate how many rows there are. – joeycozza Oct 23 '18 at 19:45
  • For more complex operations on multidimensional arrays I would recommend [data-forge](https://www.npmjs.com/package/data-forge) – cwouter Jun 26 '19 at 08:56
  • 4
    @BillyMcKee in year 2019 and Chrome 75 `loops` are 45% slower than `map`. And yes, it transposes correctly, so the second run returns initial matrix. – Ebuall Jul 19 '19 at 05:27
  • 1
    It is good practise to use underscore for the parameter you don't use in the function call. So it should be: `array[0].map((_, i) => ...` where `_` indicates the first parameter is not to be used. – Sebastian Voráč MSc. Jan 20 '20 at 11:40
  • 1
    To rotate it 90° anti-clockwise instead of clockwise, simply append `.reverse()` to theend before the very last semi-colon. – starbeamrainbowlabs Feb 19 '20 at 16:16
  • 1
    @Ebuall Actually, I ran the [jsPerf test](https://jsperf.com/transpose-2d-array) and while it is true that in Chrome 81 the `map` is 1.7x faster (equating to `for-loops` being ~42% slower than map as you said), if I run in Safari 13.1 then the `for-loops` are actually 3.8x faster, however. (This doesn't necessarily generalise, but is for transposing a very simple 4x3 matrix, using this answer's particular utilisation of `map` and the OP's particular utilisation of `for-loops` of course.) – Magne May 24 '20 at 17:48
  • 1
    @OlivierPons transposition doesn't rotate it 90º. It flips it about the `y = -x` axis – Sean Mar 24 '21 at 17:41
81

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]])
}

Even simpler Lodash/Underscore solution by Vigrant

_.unzip(matrix);

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]];
    }
  }
}
Andy
  • 10,412
  • 13
  • 70
  • 95
Yangshun Tay
  • 49,270
  • 33
  • 114
  • 141
57

here is my implementation in modern browser (without dependency):

transpose = m => m[0].map((x,i) => m.map(x => x[i]))
Mahdi Jadaliha
  • 1,947
  • 1
  • 14
  • 22
  • 1
    If you use it twice on the same array, it comes back to the first one intead of rotating 90' again – Olivier Pons Mar 03 '17 at 13:51
  • 47
    The transpose of a transpose matrix is the original matrix, refer to http://www.math.nyu.edu/~neylon/linalgfall04/project1/dj/proptranspose.htm – Mahdi Jadaliha Mar 07 '17 at 16:39
42

You could use underscore.js

_.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
Joe
  • 46,419
  • 33
  • 155
  • 245
  • 3
    That was pretty - also, underscore is more necessary to me than jQuery is. – John Strickler Jul 02 '13 at 14:52
  • or if you are using a functional library like `rambda` you can just do `const transpose = apply(zip)` – notrota Apr 26 '18 at 05:09
  • 1
    Why would this option be better than the selected answer? – Alex Lenail Jul 31 '18 at 15:46
  • This question is years old and I'm not sure it is, now. It is, though, cleaner than the [original version of the accepted answer](https://stackoverflow.com/revisions/17428705/1). You'll notice that it's been [edited substantially](https://stackoverflow.com/posts/17428705/revisions) since. Looks like it uses ES6, which I don't think was available in 2013 when the question widely was asked. – Joe Jul 31 '18 at 16:02
33

shortest way with lodash/underscore and es6:

_.zip(...matrix)

where matrix could be:

const matrix = [[1,2,3], [1,2,3], [1,2,3]];
marcel
  • 2,967
  • 1
  • 16
  • 25
  • Or, without ES6: `_.zip.apply(_, matrix)` – ach Jul 29 '15 at 20:21
  • 13
    Close but _.unzip(matrix) is shorter ;) – Vigrant Mar 18 '16 at 07:21
  • 1
    Can you expand on this? I don't get what you're saying here. That short snipplet is supposed to solve the problem? or is it just a part or what? – Julix Sep 05 '16 at 09:00
  • 2
    my gosh that's a short solution. just learned about the ... operator - used it to break down a string into an array of letters... thanks for the reply – Julix Sep 09 '16 at 17:15
14

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]]), 
    []
)
Andrew Tatomyr
  • 373
  • 3
  • 10
  • 4
    I dont know that I would call that update any better. It is clever, sure, but that is a nightmare to read. – Marie Feb 06 '20 at 03:33
10

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;
    }
  }
}
  • 2
    if your array is not a square (for example 2x8) this doesnt work I guess – Olivier Pons Mar 03 '17 at 13:54
  • 2
    This solution changes the original array. If you still need the original array then this solution might be not what you want. Other solutions create a new array instead. – Alex Aug 21 '17 at 13:43
  • Does anyone know how this is done in ES6 syntax? I tried `[arr[j][j],arr[i][j]] = [arr[i][j],arr[j][j]]` but it doesn't seem to work, am I missing something? – Nikasv Apr 22 '20 at 13:51
  • @Nikasv you likely want `[arr[j][i], arr[i][j]] = [arr[i][j], arr[j][i]]`. Note that you have some `arr[j][j]` terms which will always refer to cells on the diagonal. – Algorithmic Canary Jun 14 '20 at 20:00
9

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.

8

Another approach by iterating the array from outside to inside and reduce the matrix by mapping inner values.

const
    transpose = array => array.reduce((r, a) => a.map((v, i) => [...(r[i] || []), v]), []),
    matrix = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];

console.log(transpose(matrix));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
5

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]));

console.log(transpose([
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9, 10, 11, 12]
])); // =>  [[1,5,9],[2,6,10],[3,7,11],[4,8,12]]
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
Kevin Le - Khnle
  • 10,579
  • 11
  • 54
  • 80
4

If using RamdaJS is an option, this can be achieved in one line: R.transpose(myArray)

Rafael Rozon
  • 2,639
  • 1
  • 15
  • 27
3

Spread syntax should not be used as an alternative to push, it should only be used when you don't want to mutate the existing array.

Algorithm: For every column, just check if for that column there's a row in the resultant matrix, if there's already a row then simply push the element, else create a new row array and then push.

So, unlike many other solutions above, this solution doesn't create new arrays again and again, instead pushes onto the same array.

Also, take some time to appreciate the use of the Nullish Coalescing Operator.

const 
  transpose = arr => arr.reduce((m, r) => (r.forEach((v, i) => (m[i] ??= [], m[i].push(v))), m), []),
  matrix = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]

console.log(transpose(matrix))
Som Shekhar Mukherjee
  • 4,701
  • 1
  • 12
  • 28
2

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.
var m = CoordinateMatrix(5, 9);

// Make the matrix an irregular shape.
m[2] = m[2].slice(0, 5);
m[4].pop();

// Transpose and print the matrix.
println(formatMatrix(transpose(m)));

function Matrix(rows, cols, defaultVal) {
    return AbstractMatrix(rows, cols, function(r, i) {
        return arrayFill(cols, defaultVal);
    });
}
function ZeroMatrix(rows, cols) {
    return AbstractMatrix(rows, cols, function(r, i) {
        return zeroFill(cols);
    });
}
function CoordinateMatrix(rows, cols) {
    return AbstractMatrix(rows, cols, function(r, i) {
        return zeroFill(cols).map(function(c, j) {
            return [i, j];
        });
    });
}
function AbstractMatrix(rows, cols, rowFn) {
    return zeroFill(rows).map(function(r, i) {
        return rowFn(r, i);
    });
}
/** Matrix functions. */
function formatMatrix(matrix) {
    return matrix.reduce(function (result, row) {
        return result + row.join('\t') + '\n';
    }, '');
}
function copy(matrix) {  
    return zeroFill(matrix.length).map(function(r, i) {
        return zeroFill(getMatrixWidth(matrix)).map(function(c, j) {
            return matrix[i][j];
        });
    });
}
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);
}
/** Array fill functions. */
function zeroFill(n) {
  return new Array(n+1).join('0').split('').map(Number);
}
function arrayFill(n, defaultValue) {
    return zeroFill(n).map(function(value) {
        return defaultValue || value;
    });
}
/** Print functions. */
function print(str) {
    str = Array.isArray(str) ? str.join(' ') : str;
    return document.getElementById('out').innerHTML += str || '';
}
function println(str) {
    print.call(null, [].slice.call(arguments, 0).concat(['<br />']));
}
#out {
    white-space: pre;
}
<div id="out"></div>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
2

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())

let a = [
    [1,1,1]
    , ["_","_","1"]
]
let b = rotate(a);
let c = rotate(b);
let d = rotate(c);
console.log(`a ${a.join("\na ")}`);
console.log(`b ${b.join("\nb ")}`);
console.log(`c ${c.join("\nc ")}`);
console.log(`d ${d.join("\nd ")}`);

Yields

a 1,1,1 
a _,_,1

b _,1
b _,1
b 1,1 

c 1,_,_
c 1,1,1

d 1,1
d 1,_
d 1,_
Ro Yo Mi
  • 14,790
  • 5
  • 35
  • 43
ysle
  • 37
  • 2
2

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;
    }
Aryan Firouzian
  • 1,940
  • 5
  • 27
  • 41
2

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
}
Chang
  • 1,658
  • 1
  • 17
  • 23
2

const transpose = array => array[0].map((r, i) => array.map(c => c[i]));
console.log(transpose([[2, 3, 4], [5, 6, 7]]));
pank
  • 132
  • 1
  • 3
1

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))
.as-console-wrapper {
  max-height: 100% !important
}
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Redu
  • 25,060
  • 6
  • 56
  • 76
0
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]));
}
  • 4
    Please don't post only code as an answer, but include an explanation what your code does and how it solves the problem of the question. Answers with an explanation are generally of higher quality and more likely to attract upvotes. – Mark Rotteveel Sep 26 '19 at 17:36
0

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;
    }
Chuan Sun
  • 67
  • 8
0

Adding TS version here.

const transpose = <T>(m: Array<Array<T>>): Array<Array<T>> => m[0].map((_, i) => m.map(x => x[i]));
Karl Adler
  • 15,780
  • 10
  • 70
  • 88
0

const arr = [
  [1,2,3],
  [1,2,3],
  [1,2,3]
];

var output = arr.map((row, rowIndex) => {
  row.forEach((col, colIndex) => {
    if(colIndex > rowIndex) {
      var temp = arr[colIndex][rowIndex];
      arr[colIndex][rowIndex] = row[colIndex];
      row[colIndex] = temp;
    }
  });
  return row;
});

console.log(output);
Deepak Garg
  • 147
  • 2
  • 5
-1
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;
}
Samuel Reid
  • 1,756
  • 12
  • 22
-1

One-liner that does not change given array.

a[0].map((col, i) => a.map(([...row]) => row[i]))
Offpics
  • 273
  • 3
  • 8
-1

There is more efficient solution, in cases where n = m (number of rows equals to number of columns).

const matrix = [
   [1,1,1,1],
   [2,2,2,2],
   [3,3,3,3],
   [4,4,4,4]
];

matrix.every((r, i, a) => (
   r.every((_, j) => (
      j = a.length-j-1,
      [ r[j], a[j][i] ] = [ a[j][i], r[j] ],
      i < j-1
   )), 
   i < length-2
));

console.log(matrix);
/*
Prints:
[
   [1,2,3,4],
   [1,2,3,4],
   [1,2,3,4],
   [1,2,3,4]
]
*/

The example above will do only 6 iterations.
For bigger matrix, say 100x100 it will do 4,900 iterations, this is 51% faster than a full scan.

The principle is simple. You only need to iterate through the upper diagonal half of the matrix (Xs in the matrix below), and switch with bottom diagonal half (Os in the matrix below).

[
   [-,X,X,X],
   [O,-,X,X],
   [O,O,-,X],
   [O,O,O,-],
]

This way, you can save a lot of running time, especially in a very large matrix.

Slavik Meltser
  • 9,712
  • 3
  • 47
  • 48