JavaScript'te 2B diziyi aktarma


155

Bir dizi dizim var, şöyle bir şey:

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

Aşağıdaki dizi almak için onu devrik etmek istiyorum:

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

Döngüler kullanarak bunu programlı olarak yapmak zor değildir:

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

Ancak bu hantal görünüyor ve bunu yapmanın daha kolay bir yolu olması gerektiğini hissediyorum. Var mı?


4
İki boyutun daima aynı olacağını garanti edebilir misiniz? 1x1, 2x2, 3x3 vb. arrayLengthTam olarak kullanılan parametre nedir ? Dizideki belirli sayıda öğenin ötesine geçmediğinizden emin olmak için?
ezmek

8
Bunun JQuery ile ilgisi yok, başlığı değiştirdim.
Joe

4
Şuna bir göz atın: stackoverflow.com/questions/4492678/… . Ne yapıyorsun bir matris transpozing
stackErr

1
Evet, aktarma. Tersine çevirmek tamamen farklı olurdu ve onunla ilgilenmiyorum. Şimdilik.
ckersch

3
Sol üst-alt sağ köşegen değişmez, bu nedenle bir optimizasyon fırsatı vardır.
S Meaden

Yanıtlar:


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

mapcallbackbir dizideki her öğe için sırasıyla verilen işlevi bir kez çağırır ve sonuçlardan yeni bir dizi oluşturur. callbackyalnızca atanmış değerleri olan dizinin dizinleri için çağrılır; silinmiş veya hiç değer atanmamış dizinler için çağrılmaz.

callbacküç bağımsız değişkenle çağrılır: öğenin değeri, öğenin dizini ve çaprazlanan Array nesnesi. [kaynak]


8
Bu iyi bir çözüm. Bununla birlikte, performansı önemsiyorsanız OP'nin orijinal çözümünü kullanmalısınız (M! = N olduğu yerlerde M x N dizilerini desteklemek için hata düzeltmesi ile). bu jsPerf kontrol
Billy McKee

3
Aynı dizide iki kez kullanırsanız, tekrar 90 'döndürmek yerine ilk olana geri döner
Olivier Pons

4
neden array[0].mapyerine array.map?
John Vandivier

4
array[0].mapçünkü sütunlar olduğunu defalarca yinelemek istiyor, array.mapkaç satır olduğunu yineliyor.
joeycozza

2
2019 yılında @BillyMcKee ve Chrome 75'ten loops% 45 daha yavaş map. Ve evet, doğru bir şekilde transpozisyon yapar, böylece ikinci çalışma başlangıç ​​matrisini döndürür.
Ebuall

41

İşte benim modern tarayıcı benim uygulama (bağımlılık olmadan):

transpose = m => m[0].map((x,i) => m.map(x => x[i]))

Aynı dizide iki kez kullanırsanız, tekrar 90 'döndürmek yerine ilk olana geri döner
Olivier Pons

26
Bir devrik matrisin devri
Mehdi Jadaliha 7:17

39

Underscore.js dosyasını kullanabilirsiniz

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

3
Bu güzeldi - ayrıca, alt çizgi benim için jQuery'den daha gerekli.
John Strickler

ya da rambdayapabileceğiniz gibi işlevsel bir kütüphane kullanıyorsanızconst transpose = apply(zip)
Guy Who Knows Stuff

1
Bu seçenek neden seçilen yanıttan daha iyi olsun?
Alex Lenail

Bu soru yaşında ve şimdi olduğundan emin değilim. Bununla birlikte, kabul edilen cevabın orijinal versiyonundan daha temizdir . O zamandan beri büyük ölçüde düzenlendiğini fark edeceksiniz . Görünüşe göre ES6 kullanıyor gibi görünüyor, ki bu soru yaygın olarak sorulduğunda 2013'te mevcut değildi.
Joe

25

lodash/ underscoreve ile en kısa yol es6:

_.zip(...matrix)

nerede matrixolabilir:

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

Veya, ES6 olmadan:_.zip.apply(_, matrix)
ach

9
Kapatın ama _.unzip (matris) daha kısa;)
Vigrant

1
biraz daha açıklayabilir misin? Burada söylediğini anlamıyorum. Bu kısa parçacığın problemi çözmesi mi gerekiyor? yoksa sadece bir parçası mı, ne?
Julix

1
Allahım bu kısa bir çözüm. sadece öğrendim ... operatör - bir harf dizisine bir dize kırmak için kullanılır ... cevap için teşekkürler
Julix

22

Burada çok iyi cevaplar! Onları tek bir cevapta birleştirdim ve daha modern bir sözdizimi için bazı kodları güncelledim:

Fawad Ghafoor ve Óscar Gómez Alcañiz'den esinlenen tek gömlekler

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'in azalttığı fonksiyonel yaklaşım tarzı

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

Lodash / Undercore yazan 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]])
}

Vanilya yaklaşımı

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'dan esinlenen vanilya yerinde ES6 yaklaşımı

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

Düzgün ve saf:

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

Boş bir dizi sağlanması durumunda önceki çözümler hataya neden olabilir.

İşte bir işlev olarak:

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

Güncelleme. Forma operatörü ile daha da iyi yazılabilir:

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

2
Bu güncellemeyi daha iyi arayacağımı bilmiyorum. Zeki, elbette, ama bu okumak için bir kabus.
Marie

9

Yerinde sadece bir geçiş yaparak yapabilirsiniz:

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
diziniz bir kare değilse (örneğin 2x8) bu işe yaramaz sanırım
Olivier Pons

2
Bu çözüm orijinal diziyi değiştirir. Hala orijinal diziye ihtiyacınız varsa, bu çözüm istediğiniz şey olmayabilir. Diğer çözümler bunun yerine yeni bir dizi oluşturur.
Alex

Herkes bunun ES6 sözdiziminde nasıl yapıldığını biliyor mu? Denedim [arr[j][j],arr[i][j]] = [arr[i][j],arr[j][j]]ama işe yaramıyor, bir şey mi kaçırıyorum?
Nikasv

@Nikasv büyük olasılıkla istiyorsun [arr[j][i], arr[i][j]] = [arr[i][j], arr[j][i]]. arr[j][j]Her zaman diyagonaldeki hücrelere atıfta bulunacak bazı terimleriniz olduğunu unutmayın .
Algoritmik Kanarya

6

Sadece başka bir varyasyon kullanarak Array.map. Dizinleri kullanmak, aşağıdaki durumlarda matrisleri aktarmaya izin verir 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]; 
    }); 
});

Aktarma için gereken tek şey öğeleri önce sütun, sonra satır olarak eşlemektir.


5

Ramda JS ve ES6 sözdizimini kullanma seçeneğiniz varsa, bunu yapmanın başka bir yolu:

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
Bunu çözmek için hem Ramda'yı hem de ES6'yı kullandığına şaşkınlık
Ashwin Balamohan

1
Ramda'nın aslında bir transposeişlevi var.
Jacob Lauritzen

5

Diziyi dışarıdan içeriye yineleyerek ve iç değerleri eşleştirerek matrisi azaltarak başka bir yaklaşım.

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


Aslında! @Andrew Tatomyr yanıtını optimize ettiniz! (kriterler sizin lehinize çalışır
!;


2

Aşağıdakileri kullanarak bunu döngüler olmadan gerçekleştirebilirsiniz.

Çok zarif görünüyor ve jQuery of Underscore.js gibi herhangi bir bağımlılık gerektirmiyor .

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

küçültülmüş

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

İşte birlikte attığım bir demo. Döngü eksikliğine dikkat edin :-)

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


Neden bunu döngüsüz yapmak istemiyorsun? Döngüler olmadan yavaş
Downgoat

5
.Map bir döngü değil mi? Sadece görmüyor musun? Yani her girdinin üzerinden gidiyor ve bir şeyler yapıyor ...
Julix

2

ES6 1 katlar:

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

Óscar'ınkiyle aynı, ancak saat yönünde döndürmeyi tercih edersiniz:

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

2

Düzenleme: Bu cevap matrisi devretmez, ancak döndürür. Soruyu ilk etapta dikkatle okumadım: D

saat yönünde ve saat yönünün tersine dönüş:

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

Ancak soruya cevap vermez; Transposing gibidir ... diyagonal boyunca yansıtma (dönmüyor)
DerMike

@DerMike işaret ettiğiniz için teşekkürler. Neden bu hatayı yaptığımı bilmiyorum :) Ama en azından bazı insanlar için yararlı olduğunu görebiliyorum.
Aryan Firouzian

İşte bir matrisi döndürmek için tek satırlık kodum: stackoverflow.com/a/58668351/741251
Nitin Jadhav

1

Yukarıdaki cevapları okumayı zor buldum ya da çok ayrıntılı buldum, bu yüzden kendim yazıyorum. Ve bence bu, lineer cebirde transpoze etmenin en sezgisel yolu, değer değişimi yapmıyorsunuz , ancak her bir öğeyi yeni matristeki doğru yere yerleştiriyorsunuz:

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

Bunun biraz daha okunabilir olduğunu düşünüyorum. Kullanır Array.fromve mantık yuvalanmış döngülerle aynıdır:

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

Eşit uzunluktaki dizilerle uğraşıyorsanız, arr[0].lengthbaşka bir şeyle değiştirmeniz gerekir :

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

Dizilerinizi kısaltmayacak herhangi bir matris şekli için çalışan, TypeScript'te kitaplık içermeyen bir uygulama:

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

Verilen diziyi değiştirmeyen tek astar.

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
Lütfen yalnızca kodu yanıt olarak yayınlamayın, kodunuzun ne yaptığını ve sorunun sorununu nasıl çözdüğünü açıklayın. Açıklamalı cevaplar genellikle daha yüksek kalitededir ve yukarı oy çekme olasılığı daha yüksektir.
Mark Rotteveel

0

Beni tatmin edecek bir cevap bulamadım, bu yüzden bir tane kendim yazdım, bence anlaması ve uygulaması kolay ve tüm durumlar için uygun.

    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

Şimdiye kadar kimse burada fonksiyonel bir özyinelemeli yaklaşımdan bahsetmediğim için benim görüşüm. Haskell'in uyarlaması 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
}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.