Transposition D'un tableau 2D en JavaScript

j'ai un tableau de tableaux, quelque chose comme:

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

je voudrais le transposer pour obtenir le tableau suivant:

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

il n'est pas difficile de le faire programmatiquement 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. Est-il?

94
demandé sur Yangshun Tay 2013-07-02 18:43:06

16 réponses

array[0].map((col, i) => array.map(row => row[i]));

map appelle un callback fonction 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 des valeurs attribuées; il n'est pas invoqué pour les index qui ont été supprimés ou qui n'ont jamais reçu de valeurs.

callback est invoquée avec trois arguments: la valeur de l'élément, l'indice de l'élément, et le tableau objet étant traversé. [source]

121
répondu Fawad Ghafoor 2017-10-18 07:32:57

vous pouvez utiliser underscore.js

_.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
37
répondu Joe 2013-07-02 14:51:26

voici mon implémentation dans le navigateur moderne (sans dépendance):

transpose = m => m[0].map((x,i) => m.map(x => x[i]))
28
répondu Mahdi Jadaliha 2016-03-22 20:27:44

chemin le plus court avec lodash / underscore et es6 :

_.zip(...matrix)

matrix pourrait être:

const matrix = [[1,2,3], [1,2,3], [1,2,3]];
14
répondu marcel 2015-06-23 11:18:56

Vous pouvez le faire sur place en faisant un seul passage:

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;
    }
  }
}
9
répondu Emanuel Saringan 2013-07-02 14:59:39

pur 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 conduire à la 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]]));

mise à Jour. Il peut être écrit encore mieux avec spread operator:

const transpose = matrix => matrix.reduce(($, row) =>
    row.map((_, i) => [...($[i] || []), row[i]]), 
    []
)
8
répondu Andrew Tatomyr 2017-09-25 14:35:26

juste une autre variante en utilisant Array.map . L'utilisation des index permet de transposer les 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 la cartographie des éléments colonne-d'abord, puis par ligne.

6
répondu Óscar Gómez Alcañiz 2016-07-12 10:01:22

Beaucoup de bonnes réponses ici! Je les ai consolidés en une seule réponse et mis à jour certains du code pour une syntaxe plus moderne:

One-liners inspiré par Fawad Ghafoor et Oscar 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]));
}

approche Fonctionnelle de style avec les réduire par Andrew Tatomyr

function transpose(matrix) {
  return matrix.reduce((prev, next) => next.map((item, i) =>
    (prev[i] || []).concat(next[i])
  ), []);
}

Lodash/trait de Soulignement par 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]])
}

Vanille approche

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

Vanille en place ES6 approche inspirée par 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]];
    }
  }
}
6
répondu Yangshun Tay 2017-10-18 07:54:47

si vous avez l'option D'utiliser la syntaxe Ramda JS et ES6, alors 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>
4
répondu Kevin Le - Khnle 2016-10-05 15:38:41

Edit: cette réponse ne transposerait pas la matrice, mais la ferait tourner. Je n'ai pas lu la question attentivement en premier lieu :D

rotation dans le sens des aiguilles d'une montre et dans le sens inverse des aiguilles d'une montre:

    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;
    }
3
répondu Aryan Firouzian 2017-10-23 10:31:24

si l'utilisation de RamdaJS est une option, cela peut être réalisé en une seule ligne: R.transpose(myArray)

3
répondu Rafael Rozon 2018-02-28 05:27:40

vous pouvez réaliser ceci sans boucles en utilisant ce qui suit.

il semble très élégant et il ne nécessite pas de dépendances telles que jQuery de 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 créée. Notez l'absence 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>
2
répondu Mr. Polywhirl 2015-02-20 18:16:26

ES6 1liners as:

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

comme celui de Óscar, mais comme vous préférez le 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())
1
répondu ysle 2016-10-29 22:36:56

j'ai trouvé les réponses ci-dessus soit difficile à lire ou trop verbeux, donc j'écris moi-même. Et je pense que c'est la façon la plus intuitive de mettre en œuvre transposer en algèbre linéaire, vous ne faites pas échange de valeur , mais il suffit d'insérer chaque élément dans le 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
}
1
répondu Chang 2017-07-10 04:52:18
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;
}
0
répondu Samuel Reid 2013-07-02 14:54:24

je pense que c'est un peu plus lisible. Il utilise Array.from et la logique est identique à l'utilisation des boucles imbriquées:

var arr = [
  [1, 2, 3, 4],
  [1, 2, 3, 4],
  [1, 2, 3, 4]
];

/*
 * arr[0].length = 4 = number of result rows
 * arr.length = 3 = number of result cols
 */

var result = Array.from({ length: arr[0].length }, function(x, row) {
  return Array.from({ length: arr.length }, function(x, col) {
    return arr[col][row];
  });
});

console.log(result);

si vous avez affaire à des tableaux de longueur inégale, vous devez remplacer arr[0].length par quelque chose d'autre:

var arr = [
  [1, 2],
  [1, 2, 3],
  [1, 2, 3, 4]
];

/*
 * arr[0].length = 4 = number of result rows
 * arr.length = 3 = number of result cols
 */

var result = Array.from({ length: arr.reduce(function(max, item) { return item.length > max ? item.length : max; }, 0) }, function(x, row) {
  return Array.from({ length: arr.length }, function(x, col) {
    return arr[col][row];
  });
});

console.log(result);
0
répondu Salman A 2018-07-06 18:54:52