การโอนอาเรย์สองมิติใน JavaScript


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

mapเรียกใช้callbackฟังก์ชันที่จัดเตรียมไว้หนึ่งครั้งสำหรับแต่ละองค์ประกอบในอาร์เรย์ตามลำดับและสร้างอาร์เรย์ใหม่จากผลลัพธ์ callbackถูกเรียกเฉพาะสำหรับดัชนีของอาร์เรย์ที่มีค่าที่ได้รับมอบหมาย; มันไม่ได้ถูกเรียกใช้สำหรับดัชนีที่ถูกลบไปหรือที่ไม่เคยมีการกำหนดค่า

callback ถูกเรียกด้วยสามอาร์กิวเมนต์: ค่าขององค์ประกอบดัชนีขององค์ประกอบและวัตถุ Array ที่ถูกสำรวจ[แหล่ง]


8
นี่เป็นทางออกที่ดี อย่างไรก็ตามหากคุณสนใจเกี่ยวกับประสิทธิภาพคุณควรใช้โซลูชันดั้งเดิมของ OP (ด้วยการแก้ไขข้อบกพร่องเพื่อสนับสนุนอาร์เรย์ M x N ที่ 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

41

นี่คือการใช้งานของฉันในเบราว์เซอร์ที่ทันสมัย ​​(โดยไม่ต้องพึ่งพา):

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

ถ้าคุณใช้สองครั้งในอาเรย์เดียวกันมันจะกลับมาเป็นอันแรกที่หมุน 90 'อีกครั้ง
Olivier Pons


39

คุณสามารถใช้underscore.js

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

3
นั่นเป็นสิ่งที่ดี - และขีดเส้นใต้จำเป็นสำหรับฉันมากกว่า jQuery
John Strickler

หรือถ้าคุณกำลังใช้ห้องสมุดที่ใช้งานได้อย่างที่rambdaคุณทำได้const transpose = apply(zip)
Guy Who Knows Stuff

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

9
ปิด แต่ _.unzip (เมทริกซ์) สั้นกว่า;)
Vigrant

1
คุณขยายได้ไหม ฉันไม่ได้สิ่งที่คุณพูดที่นี่ ตัวอย่างสั้น ๆ นั้นควรจะแก้ปัญหาได้หรือไม่ หรือมันเป็นเพียงส่วนหนึ่งหรืออะไร
Julix

1
เอ้ยฉันเป็นทางออกสั้น ๆ เพิ่งเรียนรู้เกี่ยวกับตัวดำเนินการ ... - ใช้มันเพื่อแยกสตริงเป็นตัวอักษรหลายตัว ... ขอบคุณสำหรับการตอบกลับ
Julix

22

คำตอบที่ดีมากมายที่นี่! ฉันรวมมันเป็นคำตอบเดียวและอัปเดตโค้ดบางส่วนเพื่อให้มีรูปแบบที่ทันสมัยยิ่งขึ้น:

หนึ่งสมุทร - แรงบันดาลใจจาก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]));
}

รูปแบบวิธีการใช้งานพร้อมการลดโดยAndrew Tatomyr

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

Lodash / Underscore โดย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 ของวานิลลาได้รับแรงบันดาลใจจากEmanuel Saringan

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
วิธีนี้จะเปลี่ยนอาร์เรย์เดิม หากคุณยังต้องการอาเรย์ดั้งเดิมวิธีนี้อาจไม่ใช่สิ่งที่คุณต้องการ โซลูชันอื่นสร้างอาร์เรย์ใหม่แทน
อเล็กซ์

ไม่มีใครรู้ว่าสิ่งนี้จะทำในไวยากรณ์ 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]คำศัพท์บางคำที่มักจะอ้างถึงเซลล์ในแนวทแยง
Canary อัลกอริทึม

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 เพื่อแก้ปัญหานี้
Ashwin Balamohan

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ของ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);
}

minified

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

ไม่ตอบคำถามแม้ว่า; Transposing เป็นเหมือน ... สะท้อนไปตามแนวทแยง (ไม่หมุน)
DerMike

@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

การใช้งานห้องสมุดฟรีใน 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

เนื่องจากไม่มีใครกล่าวถึงวิธี recursive ทำงานที่นี่เป็นของฉัน การปรับตัวของของ Data.List.transposeHaskell

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.