Transposer un tableau 2D en JavaScript

J'ai une gamme de tableaux, quelque chose comme:

[ [1,2,3] [1,2,3] [1,2,3] ] 

J'aimerais le transposer pour obtenir le tableau suivant:

 [ [1,1,1] [2,2,2] [3,3,3] ] 

Il n'est pas difficile de le programmer en utilisant des boucles:

 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; } 

Cela, cependant, semble volumineux, et je pense qu'il devrait y avoir un moyen plus facile de le faire. Y a-t-il?

 var newArray = array[0].map(function(col, i) { return array.map(function(row) { return row[i] }) }); 

map appelle une fonction de callback fournie une fois pour chaque élément dans un tableau, dans l'ordre, et construit un nouveau tableau à partir des résultats. callback n'est invoqué que pour les index du tableau qui ont attribué des valeurs; Il n'est pas invoqué pour les index qui ont été supprimés ou qui n'ont jamais été affectés à des valeurs.

callback est invoqué avec trois arguments: la valeur de l'élément, l'index de l'élément et l'objet Array en cours de traversée. [la source]

Vous pouvez utiliser underscore.js

 _.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]]) 

Voici ma mise en œuvre dans un navigateur moderne (sans dépendance):

 transpose = m => m[0].map((x,i) => m.map(x => x[i])) 

Vous pouvez le faire en place en effectuant une seule passe:

 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; } } } 

Le plus court avec lodash / underscore et es6 :

 _.zip(...matrix) 

Où la matrix pourrait être:

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

Juste une autre variante à l'aide de Array.map . L'utilisation d'index permet de transposer des matrices où 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]; }); }); 

Tout ce qu'il y a à transposer est de mapper la colonne des éléments, d'abord, puis par ligne.

Si vous avez une option d'utilisation de la syntaxe Ramda JS et ES6, voici une autre façon de le faire:

 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> 

Rotation dans le sens horaire et antihoraire:

  function rotateCounterClockwise(a){ var n=a.length; for (var i=0; i<n/2; i++) { for (var j=i; j<ni-1; j++) { var tmp=a[i][j]; a[i][j]=a[j][ni-1]; a[j][ni-1]=a[ni-1][nj-1]; a[ni-1][nj-1]=a[nj-1][i]; a[nj-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<ni-1; j++) { var tmp=a[i][j]; a[i][j]=a[nj-1][i]; a[nj-1][i]=a[ni-1][nj-1]; a[ni-1][nj-1]=a[j][ni-1]; a[j][ni-1]=tmp; } } return a; } 

Vous pouvez le faire sans boucles en utilisant les éléments suivants.

  • Array
  • Array.prototype.map
  • Array.prototype.reduce
  • Array.prototype.join
  • String.prototype.split

Il a l'air très élégant et il ne nécessite aucune dépendance telle que 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); } 

Minimisé

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

Voici une démo que j'ai lancé ensemble. Notez le manque de boucles 🙂

 // 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> 
 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; } 

Assez et pur:

 [[0, 1], [2, 3], [4, 5]].reduce((prev, next) => next.map((item, i) => (prev[i] || []).concat(next[i]) ), []); // [[0, 2, 4], [1, 3, 5]] 

Les solutions précédentes peuvent entraîner une défaillance dans le cas où un tableau vide est fourni.

Ici, c'est comme une fonction:

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

ES6 1liners comme:

 let invert = a => a[0].map((col, c) => a.map((row, r) => a[r][c])) 

Pareil à celui d'Óscar, mais comme préférez-vous le faire tourner dans le sens des aiguilles d'une montre:

 let rotate = a => a[0].map((col, c) => a.map((row, r) => a[r][c]).reverse()) 

J'ai trouvé les réponses ci-dessus difficiles à lire ou trop détaillées, donc j'écris un moi-même. Et je pense que c'est la façon la plus intuitive d'implémenter la transposition dans l'algèbre linéaire, que vous ne faites pas d' échange de valeur , mais insérez simplement chaque élément au bon endroit dans la nouvelle matrice:

 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 }