Course: JavaScript

Progress (0%)

# Exercise: Square Matrix Class

Exercise 37 Easy

Prerequisites for the exercise

1. JavaScript Object Prototypes
2. JavaScript Object Constructors
3. All previous chapters

## Objective

Create a `SquareMatrix` class in JavaScript to create and easily work with square matrices.

## Description

In the exercises, we created a couple of functions to accomplish some common matrix operations such as addition, multiplication, rotation and transposition.

Now, you have to encapsulate all those into one single utility — the `SquareMatrix` class.

Let's explore what bells and whistles do we need to give to this class.

The `SquareMatrix` class defines two instance properties:

1. `array` — holds the array used to represent the underlying matrix.
2. `n` — holds the length of the matrix (which is same in both dimensions, since we're dealing with square matrics).

Apart from these properties, the class defines a total of six instance methods, four of which follow from the previous exercises, as described below:

1. `add(squareMatrixInstance)` — adds the calling matrix to `squareMatrixInstance` (another instance of `SquareMatrix`) and returns the sum. This method creates and returns a new matrix (instance of `SquareMatrix`). It doesn't mutate either the calling matrix or the `squareMatrixInstance` argument.
2. `multiply(squareMatrixInstance)` — multiplies the calling matrix to `squareMatrixInstance` and returns back the product. Akin to `sum()`, this method creates and returns a new matrix, and doesn't mutate either the calling matrix or the `squareMatrixInstance` argument.
3. `copy()` — copies the calling matrix and returns back the copy.
4. `fill(value)` — replaces each element of the calling matrix with `value`. In other words, the calling matrix is 'filled' up with the given `value`. The calling matrix is mutated. Nothing is returned by this method.
5. `rotateBy90()` — rotates the calling matrix clockwise by 90°. The calling matrix is mutated. Nothing is returned by this method.
6. `transpose()` — tranposes the calling matrix. The calling matrix is mutated. Nothing is returned by this method as well.

Note that the first three methods — `add()`, `multiply()` and `copy()` — are non-mutating i.e. they don't modify the original calling matrix in any way. A new matrix is returned at the end.

Similarly, the last three methods — `fill()`, `rotateBy90()` and `transpose()` — are mutating i.e. they modify the original calling matrix. Nothing is returned at the end.

When the constructor function `SquareMatrix()` is called with a single integer argument `n`, an `n` x `n` array is created with each element initialized to `0`, and this array stored in the `array` property. In the meanwhile, the property `n` is initialized as well.

Otherwise, it's assumed that the function is called with an array as argument, and henceforth, the array is used as-is to create the matrix i.e. the given array argument is set as the value of the `array` property and `n` is updated likewise.

Given all these details, you have to create this `SquareMatrix` class in JavaScript using the idea of constructors and prototypes.

## New file

Inside the directory you created for this course on JavaScript, create a new folder called Exercise-37-Square-Matrix-Class and put the .html solution files for this exercise within it.

## Solution

The specification of `SquareMatrix` class, as defined above, is quite clear to give us a clue about how to implement it.

Let's recap it. We need to have two instance properties: `array` and `n`, and six instance methods: `add()`, `multiply()`, `copy()`, `fill()`, `rotateBy90()`, `transpose()`.

First, let's define the constructor function `SquareMatrix()`:

``````function SquareMatrix(matrixArray) {

// If matrixArray is a number,
// Create an n x n matrix
if (typeof matrixArray === 'number') {
var n = matrixArray;
matrixArray = [];

for (var i = 0; i < n; i++) {
matrixArray.push([]);
for (var j = 0; j < n; j++) {
matrixArray[i].push(0);
}
}

}

this.array = matrixArray;
this.n = matrixArray.length;

}``````

In here, we check the type of the argument passed in: if it's a number, we store that number inside a local variable `n` and then create an `n` x `n` array, with all `0`s, held on in the variable `matrixArray`.

In the end, we set the instance properties `array` and `n` to `matrixArray` and the length of `matrixArray`, respectively.

The constructor's done.

Next up, we need to define all of the six instance methods.

First, let's get done with the first three non-mutating methods, of which two (`add()` and `multiply()`) are already implemented in the previous exercises.

We'll start with `add()`:

``````SquareMatrix.prototype.add = function(matrix) {
// Initializations
var matrixArray = this.array;
var secondMatrixArray = matrix.array;
var n = this.n;

// Create a new matrix to represent the sum
var sumMatrix = new SquareMatrix(n);
var sumMatrixArray = sumMatrix.array;

for (var i = 0; i < n; i++) {
for (var j = 0; j < n; j++) {
sumMatrixArray[i][j] = matrixArray[i][j] + secondMatrixArray[i][j];
}
}

return sumMatrix;
}``````

Note that from the previous implementation of `add()`, in the JavaScript Arrays — Matrix Arithmetic Exercise, we've changed quite a lot of stuff here and there and even added some new stuff.

First of all, the local variable `matrixArray` holds the `array` of the calling matrix. Note that this is the same name (i.e `matrixArray`) that we used in the constructor function `SquareMatrix()`.

When programming classes, it's always a good idea to remain consistent across variable naming inside the class' utilities. For instance, we could have used `matrixArray` in `SquareMatrix()` and then something like `thisArray` (or simply `array`) in `add()`.

Being consistent not only helps in understanding the code much better and much quicker, but also makes sure that we don't mistakenly introduce bugs into our program by accessing a variable directly when we had to access a property on that variable instead.

Moving on, then we define a variable `secondMatrixArray` to hold the `array` of the given `matrix` argument. Finally, we save `this.n` inside the variable `n`.

Next up, we create a new `n` x `n` matrix to hold the sum, store that in `sumMatrix`, and then save the value of its `array` property in `sumMatrixArray` for quick access later on inside the `for` loop.

Notice how we append the name of every variable with the word 'Array' when that variable holds the `array` of a given `SquareMatrix` instance. This is an example of consistency.

Now, let's move on to `multiply()`.

``````SquareMatrix.prototype.multiply = function(matrix) {
// Initializations
var matrixArray = this.array;
var secondMatrixArray = matrix.array;
var n = this.n;

// Create a new matrix to represent the product
var productMatrix = new SquareMatrix(n);
var productMatrixArray = productMatrix.array;

var sum;

for (var i = 0; i < n; i++) {
for (var j = 0; j < n; j++) {
sum = 0;
for (var k = 0; k < n; k++) {
sum += matrixArray[i][k] * secondMatrixArray[k][j];
}
productMatrixArray[i][j] = sum;
}
}

return productMatrix;
}``````

As you can see, the initializations here are the same as in `add()` and the naming of `productMatrix` and `productMatrix` is also quite similar to the `sumMatrix` and `sumMatrixArray`.

Moving on to `copy()`:

``````SquareMatrix.prototype.copy = function(matrix) {
// Initializations
var matrixArray = this.array;
var n = this.n;

// Create a new matrix to hold the copy
var copiedMatrix = new SquareMatrix(n);
var copiedMatrixArray = copiedMatrix.array;

for (var i = 0; i < n; i++) {
for (var j = 0; j < n; j++) {
copiedMatrixArray[i][j] = matrixArray[i][j];
}
}

return copiedMatrix;
}``````

`copy()` is very easy to implement — just iterate over the calling matrix and copy each value on to the new matrix.

Hopefully, now you're getting the gist of the naming of variables and their behavior in these methods.

With this, we are done with the definition of our three non-mutating methods. It's time to address the remaining three, mutating, methods.

First, let's consider `fill()`:

``````SquareMatrix.prototype.fill = function(value) {
// Initializations
var matrixArray = this.array;
var n = this.n;

for (var i = 0; i < n; i++) {
for (var j = 0; j < n; j++) {
matrixArray[i][j] = value;
}
}

// Return nothing
}``````

`fill()` simply iterates over the entire `matrixArray` and assigns the argument `value` to each position, effectively making every element of the calling matrix equal to `value`.

Now, over to `rotateBy90()`:

``````SquareMatrix.prototype.rotateBy90 = function() {
// Initializations
var matrixArray = this.array;
var n = this.n;

// Copy the calling matrix using its copy() method
var copiedMatrixArray = this.copy().array;

for (var i = 0; i < n; i++) {
for (var j = 0; j < n; j++) {
matrixArray[i][j] = copiedMatrixArray[n - j - 1][i];
}
}

// Return nothing
}``````

Notice how we've removed the first set of nested `for` loops (as we had previously in the final code snippet in JavaScript Arrays — Matrix Rotation Exercise) from the method's definition here and used the `copy()` method instead.

This is an example of something called code reuse.

We define the matrix copying implementation once inside the `copy()` method and then re-use from a given `SquareMatrix` instance method very conveniently.

The OOP paradigm is very good at code reuse — in fact, one of the main purposes of OOP is code reuse.

Finally, let's get done with `transpose()`:

``````SquareMatrix.prototype.transpose = function() {
// Initializations
var matrixArray = this.array;
var n = this.n;

for (var i = 0; i < n - 1; i++) {
for (var j = i + 1; j < n; j++) {
temp = matrixArray[i][j];
matrixArray[i][j] = matrixArray[j][i];
matrixArray[j][i] = temp;
}
}

// Return nothing
}``````

Altogether, our code looks something as follows:

``````function SquareMatrix(matrixArray) {
/* ... */
}

// Non-mutating methods

SquareMatrix.prototype.add = function(matrix) { /* ... */ }

SquareMatrix.prototype.multiply = function(matrix) { /* ... */ }

SquareMatrix.prototype.copy = function() { /* ... */ }

// Mutating methods

SquareMatrix.prototype.fill = function(value) { /* ... */ }

SquareMatrix.prototype.rotateBy90 = function() { /* ... */ }

SquareMatrix.prototype.transpose = function() { /* ... */ }``````

"I created Codeguage to save you from falling into the same learning conundrums that I fell into."

— Bilal Adnan, Founder of Codeguage