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
2つの寸法が常に同じであることを保証できますか?1x1、2x2、3x3など。arrayLength正確に使用されるパラメーターは何ですか。配列内の特定の数の要素を超えないようにするには?
2013

8
これはJQueryとは関係ありません。タイトルを変更しました。
Joe

4
これを確認してください:stackoverflow.com/questions/4492678/…。あなたがしていることは、行列を転置することです
stackErr

1
はい、移調します。反転は完全に異なり、私はそれに興味がありません。今のところ。
ckersch 2013

3
左上から右下の対角線は変更されていないため、最適化の機会があります。
S Meaden

回答:


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

map提供されたcallback関数を配列の要素ごとに1回ずつ順番に呼び出し、その結果から新しい配列を構築します。callback値が割り当てられている配列のインデックスに対してのみ呼び出されます。削除されたインデックス、または値が割り当てられていないインデックスに対しては呼び出されません。

callbackは、要素の値、要素のインデックス、およびトラバースされるArrayオブジェクトの3つの引数で呼び出されます。[ソース]


8
これは良い解決策です。ただし、パフォーマンスを重視する場合は、OPの元のソリューションを使用する必要があります(M!= NであるM x N配列をサポートするためのバグ修正付き)。 このjsPerfをチェックしてください
ビリーマッキー

3
同じアレイで2回使用すると、90度回転する代わりに最初のアレイに戻ります
Olivier Pons

3
なぜarray[0].map代わりにarray.map
ジョンヴァンディヴィエ2018

4
array[0].map列が存在する回数だけ繰り返したいので、array.map行がいくつあるかを繰り返します。
joeycozza

2
2019年の@BillyMcKeeとChrome 75 loopsは、45%遅いですmap。そして、はい、それは正しく転置するので、2回目の実行は初期行列を返します。
エブアル


39

あなたはunderscore.jsを使うことができます

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

3
それは素晴らしかったです-また、jQueryよりもアンダースコアが必要です。
John Strickler、2013

または、あなたがrambdaできるような関数型ライブラリを使用している場合const transpose = apply(zip)
ものを知っている人

1
このオプションが選択した回答よりも優れているのはなぜですか?
Alex Lenail

この質問はもう古いので、今はわかりません。ただし、受け入れられた回答の元のバージョンよりもきれいです。それが大幅に編集されていることに気づくでしょう。ES6を使用しているようですが、2013年に広く質問されたときは利用できなかったと思います。
Joe

25

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)の方が短い;)
Vigrant

1
これを拡張できますか?ここであなたの言っていることがわかりません。その短いスニペットは問題を解決するはずですか?それとも単なる一部なのか?
Julix

1
それは短い解決策です。...演算子について学びました-文字列を文字の配列に分解するために使用しました...返信ありがとう
Julix

22

ここに多くの良い答えがあります!私はそれらを1つの回答に統合し、より近代的な構文のためにいくつかのコードを更新しました:

ファワドガフールオスカーゴメスアルカニスからインスピレーションを得たワンライナー

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

ロダッシュ/アンダースコア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]])
}

バニラアプローチ

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

エマニュエルサリンガンに触発されたバニラインプレース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]]));

更新。 それは、spread演算子を使用してさらによく書くことができます。

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

2
私はそのアップデートをもっと良く呼ぶとは知りません。確かに賢いですが、それは読むのが悪夢です。
マリー

9

パスを1つだけ実行することで、インプレースで実行できます。

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
このソリューションは、元のアレイを変更します。それでも元のアレイが必要な場合は、この解決策が適切ではない可能性があります。他のソリューションでは、代わりに新しいアレイを作成します。
アレックス・

これが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たもう1つのバリエーション。インデックスを使用すると、行列を転置できます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関数があります
Jacob Lauritzen 2017年

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

これをループなしで実現するには、以下を使用します。

非常にエレガントに見え、Underscore.jsのjQueryなどの依存関係は必要ありません。

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はループではありませんか?見えないのは1つだけですか?つまり、すべての入力を処理し、それに対して処理を行います...
Julix

2

ES6 1liners as:

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

オスカーのものと同じですが、時計回りに回転させます。

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

@DerMike指摘してくれてありがとう。なぜ私がその間違いをしたのかはわかりません:)しかし、少なくとも他の一部の人にとっては有用であることがわかります。
Aryan Firouzian 2017年

これが、行列を回転させるための1行のコードです。stackoverflow.com
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);


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.