web-dev-qa-db-fra.com

Transposer 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],
]

Ce n'est pas difficile de le faire par programme 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 semble toutefois encombrant et j'estime qu'il devrait exister un moyen plus simple de le faire. Y a-t-il?

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

map appelle une fonction callback fournie une fois pour chaque élément d'un tableau, dans l'ordre, et construit un nouveau tableau à partir des résultats. callback n'est appelé que pour les index du tableau auxquels des valeurs ont été assignées; il n'est pas appelé pour les index qui ont été supprimés ou auxquels aucune valeur n'a été attribuée.

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 parcours. [source]

163
Fawad Ghafoor

Vous pouvez utiliser nderscore.js

_.Zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
38
Joe

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

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

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]];
20
marcel

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

Les one-liners inspirés par Fawad Ghafoor et Ó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]));
}

Style d'approche fonctionnelle avec réduction de 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]])
}

Approche vanille

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

Approche ES6 sur place à la vanille inspirée de 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]];
    }
  }
}
12
Yangshun Tay

Propre 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 à un échec 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. Cela peut être écrit encore mieux avec l'opérateur de propagation:

const transpose = matrix => matrix.reduce(($, row) =>
    row.map((_, i) => [...($[i] || []), row[i]]), 
    []
)
9
Andrew Tatomyr

Vous pouvez le faire sur place en effectuant 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
Emanuel Saringan

Juste une autre variante utilisant 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, c'est de mapper les éléments en commençant par les colonnes, puis par ligne.

6

Si RamdaJS est une option, vous pouvez le faire en une seule ligne: R.transpose(myArray)

4
Rafael Rozon

Si vous avez la possibilité d'utiliser la syntaxe Ramda JS et ES6, voici une autre façon de procéder:

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
Kevin Le - Khnle

Une autre approche consiste à itérer le tableau de l'extérieur vers l'intérieur et à réduire la matrice en mappant les valeurs internes.

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));
3
Nina Scholz

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

rotation horaire et antihoraire:

    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
Aryan Firouzian

ES6 1liners comme:

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

identique à celui d'Ó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())
2
ysle

Vous pouvez y parvenir sans boucles en utilisant ce qui suit.

Il a l’air très élégant et ne nécessite aucune dépendance telle que jQuery sur nderscore.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)}

Voici une démo que j'ai jeté 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>
2
Mr. Polywhirl

Je pense que cela est légèrement plus lisible. Il utilise Array.from et la logique est identique à l’utilisation de 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 avec 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);
1
Salman A

J'ai trouvé les réponses ci-dessus difficiles à lire ou trop verbeuses, alors j'en écris une moi-même. Et je pense que c'est la manière la plus intuitive d'implémenter transposer en algèbre linéaire, vous ne faites pas é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
}
1
Chang
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
Samuel Reid

Une implémentation sans bibliothèque dans TypeScript qui fonctionne pour toute forme de matrice qui ne tronque pas vos tableaux:

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)
}
0
pirix-gh