207

我有一个数组数组,例如:

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

我想转置它以获得以下数组:

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

使用循环以编程方式这样做并不难:

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

然而,这似乎很笨重,我觉得应该有一种更简单的方法来做到这一点。在那儿?

4

25 回答 25

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

mapcallback为数组中的每个元素按顺序调用一次提供的函数,并根据结果构造一个新数组。callback仅对具有赋值的数组索引调用;对于已被删除或从未被赋值的索引,它不会被调用。

callback使用三个参数调用:元素的值、元素的索引和被遍历的 Array 对象。[资源]

于 2013-07-02T14:48:17.927 回答
56

这是我在现代浏览器中的实现(不依赖):

transpose = m => m[0].map((x,i) => m.map(x => x[i]))
于 2016-03-22T20:27:44.573 回答
54

这里有很多好的答案!我将它们合并为一个答案,并更新了一些代码以获得更现代的语法:

受Fawad GhafoorÓ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]));
}

Andrew Tatomyr的带有 reduce 的函数式方法风格

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

Lodash/下划线 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]])
}

Vigrant 提供的更简单的 Lodash/Underscore 解决方案

_.unzip(matrix);

香草方法

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

受Emanuel Saringan启发的原地 ES6 方法

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]];
    }
  }
}
于 2017-10-18T07:54:47.563 回答
42

你可以使用underscore.js

_.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
于 2013-07-02T14:51:26.480 回答
32

lodash/underscore和的最短路径es6

_.zip(...matrix)

matrix可能在哪里:

const matrix = [[1,2,3], [1,2,3], [1,2,3]];
于 2015-06-23T11:18:56.940 回答
14

干净整洁:

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

如果提供了一个空数组,以前的解决方案可能会导致失败。

这是一个函数:

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

更新。 使用扩展运算符可以写得更好:

const transpose = matrix => matrix.reduce(
    ($, row) => row.map((_, i) => [...($[i] || []), row[i]]), 
    []
)
于 2017-01-20T21:29:49.167 回答
10

你可以通过只做一次就地做到这一点:

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;
    }
  }
}
于 2013-07-02T14:59:39.003 回答
8

另一种方法是从外到内迭代数组并通过映射内部值来减少矩阵。

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

于 2018-10-10T18:13:40.527 回答
8

只是使用Array.map. 使用索引允许转置矩阵,其中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]; 
    }); 
});

转置的所有内容都是先映射元素列,然后再逐行映射。

于 2016-07-12T10:01:22.797 回答
5

如果您可以选择使用 Ramda JS 和 ES6 语法,那么这里有另一种方法:

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>

于 2015-03-18T04:48:35.563 回答
4

如果使用RamdaJS是一种选择,这可以在一行中实现: R.transpose(myArray)

于 2018-02-28T05:27:40.780 回答
2

您可以使用以下方法在没有循环的情况下实现此目的。

它看起来非常优雅,并且不需要任何依赖,例如Underscore.jsjQuery

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

缩小

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

这是我放在一起的一个演示。注意缺少循环:-)

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

于 2015-02-20T17:29:14.067 回答
2

Spread语法不应用作 的替代方法push,仅当您不想改变现有数组时才应使用它。

算法: 对于每一列,只需检查该列的结果矩阵中是否有一行,如果已经有一行则只需push元素,否则创建一个新的行数组,然后push.

因此,与上述许多其他解决方案不同,此解决方案不会一次又一次地创建新数组,而是推送到同一个数组上。

另外,花一些时间来了解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))

于 2021-04-15T19:58:48.970 回答
2

我发现上面的答案要么难以阅读,要么太冗长,所以我自己写了一个。而且我认为这是在线性代数中实现转置的最直观的方法,您不进行值交换,而只需将每个元素插入新矩阵中的正确位置:

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
}
于 2017-07-10T04:52:18.923 回答
2

ES6 1liners 为:

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

与 Óscar 一样,但您更愿意顺时针旋转它:

let rotate = a => a[0].map((col, c) => a.map((row, r) => a[r][c]).reverse())
于 2016-10-29T22:36:56.863 回答
2

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

于 2019-12-04T07:34:56.973 回答
2

编辑:这个答案不会转置矩阵,而是旋转它。我一开始没有仔细阅读这个问题:D

顺时针和逆时针旋转:

    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;
    }
于 2016-11-11T01:31:38.133 回答
1

我认为这更具可读性。它使用Array.from和逻辑与使用嵌套循环相同:

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

如果您正在处理长度不等的数组,则需要arr[0].length用其他东西替换:

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

于 2018-07-06T17:47:39.240 回答
1

由于到目前为止没有人提到这里的功能递归方法是我的看法。改编自 Haskell 的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
}

于 2020-03-31T15:43:04.903 回答
0

不改变给定数组的单线。

a[0].map((col, i) => a.map(([...row]) => row[i]))
于 2019-09-11T14:08:54.200 回答
0

在此处添加 TS 版本。

const transpose = <T>(m: Array<Array<T>>): Array<Array<T>> => m[0].map((_, i) => m.map(x => x[i]));
于 2021-12-03T08:30:29.607 回答
0

没有找到让我满意的答案,所以自己写了一个,我觉得很容易理解和实现,适合所有情况。

    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;
    }
于 2020-02-16T05:30:58.570 回答
0

这不仅是一个超级高效的解决方案,而且是一个非常短的解决方案。

算法时间复杂度:O(n log n)

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]
]
*/

上面的示例将只进行 6 次迭代。
对于更大的矩阵,比如 100x100,它将进行 4,900 次迭代,这比此处提供的任何其他解决方案快 51%。

原理很简单,你只迭代矩阵的上对角线,因为对角线永远不会改变,下对角线与上半部分一起切换,所以没有理由也迭代它。这样,您可以节省大量运行时间,尤其是在大型矩阵中。

于 2021-04-07T15:53:03.243 回答
-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;
}
于 2013-07-02T14:54:24.530 回答
-1
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]));
}
于 2019-09-26T10:54:07.637 回答