2

If I hava a my own custom Matrix class, how can I define access to it by brackets? For example, how can I achieve:

const matrix = new Matrix(2,2)
matrix[0, 0] = 1
matrix[0, 1] = 2
matrix[1, 0] = 3
matrix[1, 1] = 4
console.log(matrix[1, 1]) // prints 4.

// or...

matrix[0][0] = 1
matrix[0][1] = 2
matrix[1][0] = 3
matrix[1][1] = 4
console.log(matrix[1][1]) // prints 4.

, assuming new Matrix(2,2) contains a 2d uninitialized array.

Jonatan
  • 1,182
  • 8
  • 20

1 Answers1

3

...how can I define access to it by brackets

You can't, in the way you've shown. JavaScript doesn't have a syntax for that; instead, it has the comma operator, which doesn't do what you want. Both 0, 1 and 1, 1 evaluate to 1 because the comma operator takes two operands, evaluates the left-hand operand, throws that value away, then evaluates the right-hand operand and takes that result as the comma operator result. So matrix[0, 1] is matrix[1].

Instead, define a method accepting two parameters, and call it (matrix.at(0, 1)), or use an array of arrays and do matrix[0][1].

You could accept a string, matrix["0,1"], by using a Proxy object and then parsing the string in the get and set traps, but I suspect that's not suitable to your use case (or most others).


You've said the width and height of the matrix are set in the constructor. You could store your fields in arrays (perhaps an array of typed arrays, depending on what you're storing in the matrix) and define properties for 0 through height - 1 that get the array for that row. So matrix[0] would be the first row of the matrix as an array / typed array, so matrix[0][0] would be the first cell of the first row. Here's a really basic example:

class Matrix {
    constructor(height, width) {
        this.data = Array.from(
            {length: height},
            () => Array.from({length: width}).fill(0)
        );
        for (let y = 0; y < height; ++y) {
            Object.defineProperty(this, y, {
                value: this.data[y]
            });
        }
    }
    toString() {
        return this.data.map(row => row.join(",")).join("\n");
    }
}

const m = new Matrix(5, 5);
m[1][2] = 1;
console.log(m.toString());

I've used defineProperty there because I figure we don't want anyone writing to these. By not supplying any of the flags (writable, configurable, enumerable) we take all the defaults, which are false.

Or with (say) a Uint8Array for each row:

class Matrix {
    constructor(height, width) {
        this.data = Array.from(
            {length: height},
            () => Uint8Array.from({length: width}).fill(0)
// Only change is ^^^^^^^^^^
        );
        for (let y = 0; y < height; ++y) {
            Object.defineProperty(this, y, {
                value: this.data[y]
            });
        }
    }
    toString() {
        return this.data.map(row => row.join(",")).join("\n");
    }
}

const m = new Matrix(5, 5);
m[1][2] = 1;
console.log(m.toString());
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks for your answer, how can I achieve matrix[0][1] for a custom class, or is that possible? – Jonatan Feb 05 '22 at 17:28
  • @Jonatan - Does the matrix have a predefined width and height? – T.J. Crowder Feb 05 '22 at 17:32
  • Yes, the height and width are defined in the constructor – Jonatan Feb 05 '22 at 17:34
  • @Jonatan - Pardon the question from someone who never studied matrixes, but would it be `[x][y]` or `[y][x]`? – T.J. Crowder Feb 05 '22 at 17:36
  • 1
    No problem, it would be [y][x] – Jonatan Feb 05 '22 at 17:39
  • @Jonatan - I've updated the answer with one possible way you could do it, by creating properties on the object for each row that reference the row array for that row. Very basic example, but hopefully useful. – T.J. Crowder Feb 05 '22 at 17:46
  • 1
    @Jonatan - I'm glad that helps! :-) I just edited the answer to reverse the order of the constructor parameters. I figure if it's `[y][x]`, the constructor should probably be `(height, width)` but I had it the other way around originally. Happy coding! – T.J. Crowder Feb 05 '22 at 17:54
  • 2
    (As an exercise for myself) I've made an example that uses a `Proxy`: https://jsfiddle.net/yxhw8ba3/ – Andreas Feb 05 '22 at 18:08