0
\$\begingroup\$

I needed to rotate a non-square matrix in my program to transpose some spreadsheet data and decided to make a function to rotate 90, 180, or 270 degrees.

It doesn't rotate the matrix in place, but instead generates a copy and returns that. I want to go for processing efficiency, let me know what I can do better:

//Rotates the provided matrix by provided degrees counterclockwise
function RotateMatrix(matrix, degrees){  
  if(degrees == 90){
    //For 90 degrees swap the height/width and swap the location of each element
    var output = GenerateMatrix(matrix[0].length, matrix.length, 0); //Swapping the width and height for non square matrices
    for(var i = 0; i < matrix[0].length; i++){
      for(var j = 0; j < matrix.length; j++){
        output[i][j] = matrix[j][i];
      }
    }    
  } else if(degrees == 180) {
    //For 180 degrees, rebuild array backwards
    var output = GenerateMatrix(matrix.length, matrix[0].length, 0);
    for(var i = matrix.length - 1; i >= 0; i--){
      for(var j = matrix[0].length - 1; j >=0; j--){
        output[matrix.length - 1 - i][matrix[0].length - 1 - j] = matrix[i][j];
      }
    }    
  } else if(degrees == 270) {
    //For 270 degrees, not sure how to make short description
    var output = GenerateMatrix(matrix[0].length, matrix.length, 0); //Swapping the width and height for non square matrices
    for(var i = 0; i < matrix[0].length; i++){
      for(var j = matrix.length - 1; j >=0; j--){
        output[i][matrix.length - 1 - j] = matrix[j][i];
      }
    }      
  }   
  return output;
}

//Generates a matrix with the requested length and width and value
function GenerateMatrix(length, width, value){
  var output = [];
  for(var i = 0; i < length; i++){
    width > 0 ? output.push([]) : output.push(value); //If matrix has 0 width
    for(var j = 0; j < width; j++){
      output[i].push(value)
    }
  }
  return output;
}
\$\endgroup\$
2
\$\begingroup\$

Unnecessary matrix generation

In JavaScript, Arrays don't have a fixed size. For example, you could do this:

var arr = [];
arr[324] = 91;

with no problem.

Your GenerateMatrix function is really over-doing it. All you need to do is create the arrays for the rows/columns then change their values.

And, you can do that with a neat trick:

Array.apply(null, Array(rowCount)).map(function(){return []});
\$\endgroup\$
  • \$\begingroup\$ Thanks for the reply. I've typically programmed in C#, so JS is pretty new to me. That's good to know that I don't need to generate the "blank" array first. I initially avoided map due to the performance hit, looping ended up being more performance friendly when this is being executed in a cloud environment with very limited processing time. \$\endgroup\$ – Douglas Gaskell Apr 10 '16 at 23:55
1
\$\begingroup\$

Here's a version to where it does not flip, but instead reads or writes into the correct location. Fastest way I know to "rotate" it, but does add some overhead when reading and writing.

function addC(row, i) {
    Object.defineProperty(row.self, i, {
        get: function() { 
             if(!(row.matrix.degrees % 360)) {
                 return row.data[i];
             }
             else if(!(row.matrix.degrees % 270)) {
             }
             else if(!(row.matrix.degrees % 180)) {
             }
             else if(!(row.matrix.degrees % 90)) {
                 return row.matrix.rows[i].data[row.matrix.column];
             }
        },
        set: function(v) { 
             if(!(row.matrix.degrees % 360)) {
                 row.data[i] = v;
             }
             else if(!(row.matrix.degrees % 270)) {
             }
             else if(!(row.matrix.degrees % 180)) {
             }
             else if(!(row.matrix.degrees % 90)) {
                 row.matrix.rows[i].data[row.matrix.column] = v;
             }
        },
        enumerable: true
    });
}

function Row(matrix) {
    var members = {
        self: this,
        matrix: matrix,
        data: {}
    }
    for(var i = matrix.width; i--;) {
        addC(members, i);
    }
    Object.defineProperty(members.self, "data", { value: members.data });
}

function addR(matrix, i) {
    matrix.rows[i] = new Row(matrix);
    Object.defineProperty(matrix.self, i, {
        get: function() { matrix.column = i; return matrix.rows[i]; },
        enumerable: true
    });
}

function Matrix(width, height) {
    var members = {
        self: this,
        width: width,
        height: height,
        degrees: 0,
        column: -1,
        rows: {}
    };

    for(var i = height; i--;) {
        addR(members, i);
    }
    Object.defineProperty(members.self, "degrees", { value: function(v) {
        if(v === undefined) return members.degrees;
        // Can add more checks if need be.
        if(!(v % 90)) members.degrees = v;
        return members.self;
    }});
}

You probably know more about the rotations than I do so I did the ones that I was 100% sure were correct. It is pretty straight forward, when the matrix object is accessed it returns a row object that then figures out what data you actually meant.

var matrix = new Matrix(2, 2);
matrix[0][1] = 12;
matrix.degrees(90);
console.log(matrix[1][0]); // => 12

Hope this helps!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.