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와 관련이 없으며 제목을 변경했습니다.
Joe

4
이 체크 아웃 : stackoverflow.com/questions/4492678/...를 . 당신이하고있는 것은 행렬을 바꾸는 것입니다.
stackErr

1
그렇습니다. 반전은 완전히 다르며 관심이 없습니다. 지금은
ckersch

3
왼쪽 상단에서 오른쪽 하단 대각선은 변경되지 않으므로 최적화 기회가 있습니다.
S Meaden

답변:


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

mapcallback배열의 각 요소에 대해 제공된 함수를 순서대로 한 번 호출 하고 결과에서 새 배열을 구성합니다. callback값이 할당 된 배열의 인덱스에 대해서만 호출됩니다. 삭제되었거나 값이 할당되지 않은 인덱스에 대해서는 호출되지 않습니다.

callback요소의 값, 요소의 색인 및 순회되는 Array 객체의 세 가지 인수로 호출됩니다. [출처]


8
이것은 좋은 해결책입니다. 그러나 성능에 관심이 있다면 OP의 원래 솔루션을 사용해야합니다 (M! = N 인 M x N 어레이를 지원하는 버그 수정 포함). 이 jsPerf를 확인하십시오
Billy McKee

3
같은 배열에서 두 번 사용하면 다시 90 '회전하는 최초의 intead로 돌아옵니다
Olivier Pons

3
array[0].map대신에 array.map?
John Vandivier

4
array[0].map그는 열이 여러 번 반복되기를 원하기 때문에 array.map행이 몇 줄인지 반복합니다.
joeycozza

2
2019 년의 @BillyMcKee 및 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 Lenail

이 질문은 오래되었지만 지금은 확실하지 않습니다. 그러나 허용 된 답변원래 버전 보다 깨끗합니다 . 그 이후 로 편집 된 것을 알 수 있습니다. ES6을 사용하는 것 같습니다 .2013 년에 질문이 널리 퍼 졌을 때 사용할 수 없었습니다.
Joe

24

lodash/ underscore와 가장 짧은 방법 es6:

_.zip(...matrix)

어디 matrix있을 수 있습니다 :

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

또는 ES6없이 :_.zip.apply(_, matrix)
ach

9
닫기 _.unzip (matrix) is short;)
Vigrant

1
이것을 확장 할 수 있습니까? 나는 당신이 여기서 말하는 것을 얻지 못합니다. 그 짧은 니플이 문제를 해결하기로되어 있습니까? 아니면 단지 일부입니까?
Julix

1
내 세상에 그것은 짧은 해결책입니다. 방금 ... 연산자에 대해 배웠습니다-문자열을 문자 배열로 나누는 데 사용되었습니다. 답장을 보내 주셔서 감사합니다
Julix

21

여기에 많은 좋은 답변이 있습니다! 나는 그것들을 하나의 답변으로 통합하고보다 현대적인 구문을 위해 코드를 업데이트했습니다.

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 / 밑줄 마르셀

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
나는 그 업데이트를 더 잘 부를 것이라는 것을 모른다. 그것은 영리하지만 확실히 읽을 수있는 악몽입니다.
Marie

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와 ES6를 모두 사용하는 것에 대한
굉장함

1
Ramda 실제로이 transposeα- 함수를 지금.
Jacob Lauritzen

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

축소

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 1 라이너 :

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

편집 :이 답변은 행렬을 바꾸지 않고 회전시킵니다. 나는 처음부터 질문을주의 깊게 읽지 않았습니다.

시계 방향 및 반 시계 방향 회전 :

    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

@DerMike 지적 해 주셔서 감사합니다. 나는 왜 내가 그 실수를했는지 모르겠다 :) 적어도 나는 그것이 다른 사람들에게 유용하다는 것을 알 수 있습니다.
Aryan Firouzian

여기에 매트릭스를 회전 나의 한 줄의 코드는 다음과 같습니다 stackoverflow.com/a/58668351/741251
니틴 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);


1

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


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

나는 나를 만족시키는 대답을 찾지 못했기 때문에 직접 작성했습니다. 이해하고 구현하기 쉽고 모든 상황에 적합하다고 생각합니다.

    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

지금까지 아무도 기능 재귀 접근 방식을 언급하지 않았으므로 여기에 내 취향이 있습니다. 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
}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.