Skip to content

Commit

Permalink
feat: allows to select only rows or columns as view (#51)
Browse files Browse the repository at this point in the history
* feat: allows to select only rows or columns as view

* test(selectionView): handle typed arrays

* test: remove should

* test: explicit message on throws
  • Loading branch information
maasencioh authored Jul 17, 2017
1 parent 7519323 commit 46eb916
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 31 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
node_modules
/matrix.js
coverage/
2 changes: 1 addition & 1 deletion __tests__/matrix/creation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'should';

import Matrix from '../../src';
import MatrixTransposeView from '../../src/views/transpose';
import * as util from '../../test/util';
import * as util from '../../testUtils';

describe('Matrix creation', function () {
it('should create a new object', function () {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/matrix/utility.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'should';

import Matrix from '../../src';
import * as util from '../../test/util';
import * as util from '../../testUtils';

describe('utility methods', function () {
var matrix;
Expand Down
38 changes: 22 additions & 16 deletions __tests__/views/selection.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
import 'should';

import Matrix from '../../src';

describe('Selection view', function () {
it('should correctly remap coordinates', function () {
describe('Selection view', () => {
it('should correctly remap coordinates', () => {
const m = Matrix.ones(5, 8);
const msv = m.selectionView([1, 2], [2, 1]);

m.get(1, 2).should.equal(1);
expect(m.get(1, 2)).toBe(1);
msv.set(0, 0, 5);
m.get(1, 2).should.equal(5);
expect(m.get(1, 2)).toBe(5);

m.get(2, 1).should.equal(1);
expect(m.get(2, 1)).toBe(1);
m.set(2, 1, 10);
msv.get(1, 1).should.equal(10);
expect(msv.get(1, 1)).toBe(10);
});

it('should throw when wrong arguments or range', function () {
const m = Matrix.ones(2, 2);
(function () {
m.selectionView([1, 1, 2], [0, 2]);
}).should.throw(RangeError);
it('should handle typed arrays', () => {
const m = Matrix.ones(5, 8);
const msv = m.selectionView(Int8Array.from([1, 2]), Int8Array.from([2, 1]));

expect(m.get(1, 2)).toBe(1);
msv.set(0, 0, 5);
expect(m.get(1, 2)).toBe(5);

(function () {
m.selectionView([1, 1, 2]);
}).should.throw(TypeError);
expect(m.get(2, 1)).toBe(1);
m.set(2, 1, 10);
expect(msv.get(1, 1)).toBe(10);
});

it('should throw when wrong arguments or range', () => {
const m = Matrix.ones(2, 2);
expect(() => m.selectionView([1, 1, 2], [0, 2])).toThrow(RangeError);
expect(() => m.selectionView([1, 1])).toThrow(TypeError);
});
});
18 changes: 18 additions & 0 deletions __tests__/views/selectionColumn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Matrix from '../../src';

describe('Selection column view', () => {
it('should correctly remap coordinates', () => {
const m = Matrix.ones(5, 8);
const msv = m.columnSelectionView([1, 2]);

expect(m.get(0, 2)).toBe(1);
msv.set(0, 1, 5);
expect(m.get(0, 2)).toBe(5);
});

it('should throw when wrong arguments or range', () => {
const m = Matrix.ones(2, 2);
expect(() => m.columnSelectionView([1, 1, 2])).toThrow('column indices are out of range');
expect(() => m.columnSelectionView(1)).toThrow('unexpected type for column indices');
});
});
18 changes: 18 additions & 0 deletions __tests__/views/selectionRow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Matrix from '../../src';

describe('Selection view', () => {
it('should correctly remap coordinates', () => {
const m = Matrix.ones(5, 8);
const msv = m.rowSelectionView([1, 2]);

expect(m.get(1, 0)).toBe(1);
msv.set(0, 0, 5);
expect(m.get(1, 0)).toBe(5);
});

it('should throw when wrong arguments or range', () => {
const m = Matrix.ones(2, 2);
expect(() => m.rowSelectionView([1, 1, 2])).toThrow('row indices are out of range');
expect(() => m.rowSelectionView(1)).toThrow('unexpected type for row indices');
});
});
26 changes: 26 additions & 0 deletions src/abstractMatrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import MatrixTransposeView from './views/transpose';
import MatrixRowView from './views/row';
import MatrixSubView from './views/sub';
import MatrixSelectionView from './views/selection';
import MatrixRowSelectionView from './views/rowSelection';
import MatrixColumnSelectionView from './views/columnSelection';
import MatrixColumnView from './views/column';
import MatrixFlipRowView from './views/flipRow';
import MatrixFlipColumnView from './views/flipColumn';
Expand Down Expand Up @@ -1506,6 +1508,30 @@ export default function AbstractMatrix(superCtor) {
return new MatrixSelectionView(this, rowIndices, columnIndices);
}

/**
* Returns a view of the row indices
* @example
* // resulting vector is [[1,2,3], [1,2,3]]
* var matrix = new Matrix([[1,2,3], [4,5,6]]).rowSelectionView([0, 0])
* @param {Array<number>} rowIndices
* @return {MatrixRowSelectionView}
*/
rowSelectionView(rowIndices) {
return new MatrixRowSelectionView(this, rowIndices);
}

/**
* Returns a view of the column indices
* @example
* // resulting vector is [[2, 2], [5, 5]]
* var matrix = new Matrix([[1,2,3], [4,5,6]]).columnSelectionView([1, 1])
* @param {Array<number>} columnIndices
* @return {MatrixColumnSelectionView}
*/
columnSelectionView(columnIndices) {
return new MatrixColumnSelectionView(this, columnIndices);
}


/**
* Calculates and returns the determinant of a matrix as a Number
Expand Down
41 changes: 29 additions & 12 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,29 +65,46 @@ export function checkColumnVector(matrix, vector) {
}

export function checkIndices(matrix, rowIndices, columnIndices) {
return {
row: checkRowIndices(matrix, rowIndices),
column: checkColumnIndices(matrix, columnIndices)
};
}

export function checkRowIndices(matrix, rowIndices) {
if (typeof rowIndices !== 'object') {
throw new TypeError('unexpected type for row indices');
}

var rowOut = rowIndices.some(r => {
return r < 0 || r >= matrix.rows;

});

if (rowOut) {
throw new RangeError('row indices are out of range');
}

if (!Array.isArray(rowIndices)) rowIndices = Array.from(rowIndices);

return rowIndices;
}

export function checkColumnIndices(matrix, columnIndices) {
if (typeof columnIndices !== 'object') {
throw new TypeError('unexpected type for column indices');
}

var columnOut = columnIndices.some(c => {
return c < 0 || c >= matrix.columns;
});

if (rowOut || columnOut) {
throw new RangeError('Indices are out of range');
if (columnOut) {
throw new RangeError('column indices are out of range');
}
if (!Array.isArray(columnIndices)) columnIndices = Array.from(columnIndices);

if (typeof rowIndices !== 'object' || typeof columnIndices !== 'object') {
throw new TypeError('Unexpected type for row/column indices');
}
if (!Array.isArray(rowIndices)) rowIndices = Array.from(rowIndices);
if (!Array.isArray(columnIndices)) rowIndices = Array.from(columnIndices);

return {
row: rowIndices,
column: columnIndices
};
return columnIndices;
}

export function checkRange(matrix, startRow, endRow, startColumn, endColumn) {
Expand Down
19 changes: 19 additions & 0 deletions src/views/columnSelection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import BaseView from './base';
import {checkColumnIndices} from '../util';

export default class MatrixColumnSelectionView extends BaseView {
constructor(matrix, columnIndices) {
columnIndices = checkColumnIndices(matrix, columnIndices);
super(matrix, matrix.rows, columnIndices.length);
this.columnIndices = columnIndices;
}

set(rowIndex, columnIndex, value) {
this.matrix.set(rowIndex, this.columnIndices[columnIndex], value);
return this;
}

get(rowIndex, columnIndex) {
return this.matrix.get(rowIndex, this.columnIndices[columnIndex]);
}
}
19 changes: 19 additions & 0 deletions src/views/rowSelection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import BaseView from './base';
import {checkRowIndices} from '../util';

export default class MatrixRowSelectionView extends BaseView {
constructor(matrix, rowIndices) {
rowIndices = checkRowIndices(matrix, rowIndices);
super(matrix, rowIndices.length, matrix.columns);
this.rowIndices = rowIndices;
}

set(rowIndex, columnIndex, value) {
this.matrix.set(this.rowIndices[rowIndex], columnIndex, value);
return this;
}

get(rowIndex, columnIndex) {
return this.matrix.get(this.rowIndices[rowIndex], columnIndex);
}
}
2 changes: 1 addition & 1 deletion test/util.js → testUtils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Matrix from '../src';
import Matrix from './src/index';

export function getSquareArray() {
return [
Expand Down

0 comments on commit 46eb916

Please sign in to comment.