Transposición de una matriz 2D en JavaScript
arrays matrix (15)
¡Muchas buenas respuestas aquí! Los consolidé en una respuesta y actualicé algunos de los códigos para una sintaxis más moderna:
One-liners inspirados por y Ó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]));
}
Estilo de enfoque funcional con reducir por
function transpose(matrix) {
return matrix.reduce((prev, next) => next.map((item, i) =>
(prev[i] || []).concat(next[i])
), []);
}
Lodash / Underscore por
function tranpose(matrix) {
return _.zip(...matrix);
}
// Without spread operator.
function transpose(matrix) {
return _.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
}
Enfoque de vainilla
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;
}
Enfoque vainilla in situ ES6 inspirado en
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]];
}
}
}
Tengo una matriz de matrices, algo así como:
[
[1,2,3],
[1,2,3],
[1,2,3],
]
Me gustaría transponerlo para obtener la siguiente matriz:
[
[1,1,1],
[2,2,2],
[3,3,3],
]
No es difícil hacerlo programáticamente usando bucles:
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;
}
Esto, sin embargo, parece voluminoso, y siento que debería haber una manera más fácil de hacerlo. ¿Esta ahí?
ES6 1liners como:
let invert = a => a[0].map((col, c) => a.map((row, r) => a[r][c]))
igual que la de Óscar, pero como prefieres rotarla en el sentido de las agujas del reloj:
let rotate = a => a[0].map((col, c) => a.map((row, r) => a[r][c]).reverse())
Las respuestas anteriores me parecieron difíciles de leer o demasiado detalladas, así que escribo una. Y creo que esta es la forma más intuitiva de implementar la transposición en álgebra lineal, no haces intercambio de valores , sino que simplemente insertas cada elemento en el lugar correcto en la nueva matriz:
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
}
Limpio y puro:
[[0, 1], [2, 3], [4, 5]].reduce((prev, next) => next.map((item, i) =>
(prev[i] || []).concat(next[i])
), []); // [[0, 2, 4], [1, 3, 5]]
Las soluciones anteriores pueden provocar fallas en caso de que se proporcione una matriz vacía.
Aquí está como una función:
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]]));
Actualizar. Se puede escribir aún mejor con el operador de propagación:
const transpose = matrix => matrix.reduce(($, row) =>
row.map((_, i) => [...($[i] || []), row[i]]),
[]
)
Otra variación más usando Array.map
. El uso de índices permite transponer matrices donde 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];
});
});
Todo lo que hay que transponer es mapear los elementos columna primero y luego fila.
Podría usar underscore.js
_.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
Puede lograr esto sin bucles utilizando lo siguiente.
Se ve muy elegante y no requiere dependencias como 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);
}
Minificado
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)}
Aquí hay una demo que lancé. Observe la falta de bucles :-)
// 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>
Puedes hacerlo in situ con solo un pase:
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;
}
}
}
Si tiene la opción de utilizar la sintaxis de Ramda JS y ES6, esta es otra forma de hacerlo:
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>
Si usar transpose-function es una opción, esto se puede lograr en una línea: R.transpose(myArray)
aquí está mi implementación en un navegador moderno (sin dependencia):
transpose = m => m[0].map((x,i) => m.map(x => x[i]))
forma más corta con lodash
/ underscore
y es6
:
_.zip(...matrix)
donde la matrix
podría ser:
const matrix = [[1,2,3], [1,2,3], [1,2,3]];
Editar: Esta respuesta no transpone la matriz, sino que la rota. No leí la pregunta con cuidado en primer lugar: D
rotación en sentido horario y antihorario:
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;
}
array[0].map((col, i) => array.map(row => row[i]));
map
llama una función decallback
proporcionada una vez para cada elemento en una matriz, en orden, y construye una nueva matriz a partir de los resultados.callback
se invoca solo para los índices de la matriz que tienen valores asignados; no se invoca para los índices que se han eliminado o para los que nunca se han asignado valores.
callback
se invoca con tres argumentos: el valor del elemento, el índice del elemento y el objeto Array que se está recorriendo. [source]
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;
}