用JavaScript转置2D数组


154

我有一个数组数组,就像:

[
    [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
您可以保证两个尺寸始终相同吗?1x1、2x2、3x3等arrayLength。确切地使用什么参数?为了确保您不超出数组中一定数量的元素?
暗恋

8
这与JQuery无关,我更改了标题。
2013年

4
检查一下:stackoverflow.com/questions/4492678/…。您正在做的是转置矩阵
stackErr 2013年

1
是的,移调。反相将完全不同,我对此不感兴趣。目前。
ckersch

3
左上角到右下角对角线不变,因此存在优化机会。
S Meaden

Answers:


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

mapcallback依次为数组中的每个元素调用提供的函数,然后从结果中构造一个新数组。callback仅针对已分配值的数组索引调用;对于已删除或从未分配值的索引,不会调用它。

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


8
这是一个很好的解决方案。但是,如果您关心性能,则应该使用OP的原始解决方案(带有错误修复程序,以支持M xN个数组,其中M!= N)。 检查此jsPerf
Billy McKee,

3
如果在同一阵列上使用两次,它将再次回到旋转90'的第一个界面
Olivier Pons

3
为什么要array[0].map代替array.map
John Vandivier

4
array[0].map因为他想迭代有多少列,所以array.map要迭代有多少行。
joeycozza

2
@BillyMcKee在2019年和Chrome 75 loops比慢45%map。是的,它转置正确,因此第二次运行返回初始矩阵。
Ebuall


39

您可以使用underscore.js

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

3
这很漂亮-而且,下划线对我来说比jQuery更重要。
约翰·斯特里克勒

或者,如果您正在使用功能库,就像rambda您可以做的那样const transpose = apply(zip)
知道资料的家伙

1
为什么该选项比所选答案更好?
Alex Lenail18年

这个问题已经有多年历史了,我现在不确定。但是,它比接受的答案原始版本干净。您会注意到,此后已对其进行了实质性编辑。看起来它使用的是ES6,我不认为它在2013年被广泛问到该问题时就不可用。

24

lodash/ underscore和的最短方法es6

_.zip(...matrix)

matrix可能在哪里:

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

或者,没有ES6:_.zip.apply(_, matrix)
2015年

9
关闭,但_.unzip(matrix)更短;)
激进的

1
您可以对此进行扩展吗?我听不懂你在说什么。那段简短的代码应该可以解决问题?还是仅仅是一部分?
2016年

1
我的天哪,这是一个简短的解决方案。刚刚了解了...运算符-使用它将字符串分解为字母数组...感谢您的答复
Julix

21

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

Fawad GhafoorÓscarGómezAlcañ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]));
}

减少功能的方法风格by Andrew Tatomyr

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

Lodash /下划线的烫发

function tranpose(matrix) {
  return _.zip(...matrix);
}

// Without spread operator.
function transpose(matrix) {
  return _.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
}

香草方法

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

11

干净利落:

[[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]]), 
    []
)

2
我不知道我会称之为更好的更新。当然,这很聪明,但这是一场噩梦。
玛丽

9

您只需做一遍就可以就地完成它:

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

1
如果您的阵列不是正方形(例如2x8),则无法正常工作
Olivier Pons

2
此解决方案更改了原始数组。如果仍然需要原始阵列,则可能不需要此解决方案。其他解决方案将创建一个新数组。
亚历克斯(Alex)

有人知道ES6语法是如何做到的吗?我尝试过,[arr[j][j],arr[i][j]] = [arr[i][j],arr[j][j]]但似乎不起作用,我缺少什么吗?
Nikasv

您可能想要的@Nikasv [arr[j][i], arr[i][j]] = [arr[i][j], arr[j][i]]。请注意,您有一些arr[j][j]术语将始终引用对角线上的单元格。
算法金丝雀

6

只是使用的另一个变体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]; 
    }); 
});

转置的全部操作是先将元素映射到列,然后再按行映射。


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>


2
使用
Ramda

1
Ramda实际上现在具有transpose-函数
雅各布•劳里岑

5

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

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


确实!您已经优化了@Andrew Tatomyr的答案!(基准对您有利!!)
scraaappy


2

您可以使用以下方法实现无循环运行。

它看起来非常优雅,它不需要任何相关性,如jQuery的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);
}

精制

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>


为什么您不希望没有循环就这样做?没有循环会很慢
Downgoat

5
.map不是循环吗?只是你看不到一个?我的意思是,它遍历了每个输入并对其进行了处理……
Julix

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

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

但是不回答问题;转置就像...沿着对角线镜像(不旋转)
DerMike '17

@DerMike感谢您指出。我不知道为什么会犯这样的错误:)但至少我可以看到它对其他人有用。
Aryan Firouzian

这是我用于旋转矩阵的一线代码:stackoverflow.com/a/58668351/741251
Nitin Jadhav

1

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

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

我认为这更具可读性。它使用的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);


0
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

TypeScript中的无库实现,适用于不会截断数组的任何矩阵形状:

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

不改变给定数组的单线。

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

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

3
请不要只发布代码作为答案,而应提供解释,说明代码的作用以及如何解决问题。带有解释的答案通常质量更高,更容易吸引反对意见。
Mark Rotteveel

0

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


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

0

由于到目前为止,没有人提到功能递归方法,因此我认为。哈斯克尔定律的改编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
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.