Skip to content

Commit

Permalink
more datastructures
Browse files Browse the repository at this point in the history
  • Loading branch information
spalberg committed Dec 7, 2024
1 parent 7f0e363 commit 691673a
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 79 deletions.
11 changes: 8 additions & 3 deletions lib/datastructures/grid/grid.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { Vector } from "../vector/mod.ts";
import { ArrayVector } from "../vector/vector.ts";

export class Grid<T> {
#data: Array<Array<T>>;

constructor(data: Array<Array<T>>) {
private constructor(data: Array<Array<T>>) {
this.#data = data;
this.#validatekDimensions();
}

static from<T>(data: Array<Array<T>>): Grid<T> {
return new Grid(data);
}

static fromStrings(strings: Array<string>): Grid<string> {
return new Grid(strings.map((line) => line.split("")));
}
Expand All @@ -26,7 +31,7 @@ export class Grid<T> {

*rows(): Generator<Vector<T>> {
for (const row of this.#data) {
yield new Vector(row);
yield ArrayVector.from(row);
}
}

Expand All @@ -51,7 +56,7 @@ export class GridColumnVector<T> extends Vector<T> {
#x: number;

constructor(grid: Grid<T>, x: number) {
super([]);
super();
this.#grid = grid;
this.#x = x;
}
Expand Down
68 changes: 34 additions & 34 deletions lib/datastructures/grid/grid_test.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,51 @@
import { expect } from "@std/expect";
import { describe, it } from "@std/testing/bdd";
import { Grid } from "./grid.ts";
import { vectorImplTestSuite } from "../vector/vector_test.ts";
import { Grid, GridColumnVector } from "./grid.ts";

const defaultGrid = new Grid([
[1, 2, 3],
[4, 5, 6],
]);
describe("Grid", () => {
const smallTestGrid = Grid.from([
[1, 2, 3],
[4, 5, 6],
]);

describe("Grid::new", () => {
it("should handle well formed 2d arrays", () => {
expect(defaultGrid.height).toBe(2);
expect(defaultGrid.width).toBe(3);
});

it("should throw on malformed 2d arrays", () => {
it("factory from", () => {
expect(smallTestGrid.height).toBe(2);
expect(smallTestGrid.width).toBe(3);
expect(() => {
new Grid([
Grid.from([
[1, 2, 3],
[4, 5],
]);
}).toThrow("Rows have different lengths");
});

it("should throw on empty 2d arrays", () => {
expect(() => {
new Grid([]);
Grid.from([]);
}).toThrow("Grid is empty");
});
});

describe("Grid.fromStrings", () => {
it("should handle well formed strings", () => {
it("factory fromStrings", () => {
const grid = Grid.fromStrings(["123", "456"]);
expect(grid.height).toBe(2);
expect(grid.width).toBe(3);
});
});

describe("Grid::at", () => {
it("should return the value at the given coordinates", () => {
expect(defaultGrid.at(1, 1)).toBe(5);
expect(defaultGrid.at(2, 0)).toBe(3);
expect(defaultGrid.at(2, 2)).toBe(null);
it("method at", () => {
expect(smallTestGrid.at(1, 1)).toBe(5);
expect(smallTestGrid.at(2, 0)).toBe(3);
expect(smallTestGrid.at(2, 2)).toBe(null);
});
});

describe("Grid::rows", () => {
it("should yield rows", () => {
const rows = [...defaultGrid.rows()];
it("method rows", () => {
const rows = [...smallTestGrid.rows()];
expect(rows.length).toBe(2);
expect(rows[0].length).toBe(3);
expect(rows[0].at(0)).toBe(1);
expect(rows[1].length).toBe(3);
expect(rows[1].at(0)).toBe(4);
});
});

describe("Grid::columns", () => {
it("should yield columns", () => {
const columns = [...defaultGrid.columns()];
it("method columns", () => {
const columns = [...smallTestGrid.columns()];
expect(columns.length).toBe(3);
expect(columns[0].length).toBe(2);
expect(columns[0].at(0)).toBe(1);
Expand All @@ -67,3 +54,16 @@ describe("Grid::columns", () => {
expect(columns[1].at(0)).toBe(2);
});
});

describe("GridColumnVector", () => {
const grid = Grid.from([
[0, 1, 0],
[0, 2, 0],
[0, 3, 0],
[0, 4, 0],
[0, 5, 0],
[0, 6, 0],
]);
const gridColumn = new GridColumnVector(grid, 1);
vectorImplTestSuite(gridColumn);
});
117 changes: 98 additions & 19 deletions lib/datastructures/vector/vector.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
export class Vector<T> {
#data: Array<T>;
#start: number;
#end: number;

constructor(
data: Array<T>,
start: number = 0,
end: number = data.length,
) {
this.#data = data;
this.#start = start;
this.#end = end;
export abstract class Vector<T> {
static from<T>(data: Array<T>): Vector<T> {
return new ArrayVector(data);
}

static fromString(data: string): Vector<string> {
return new Vector(data.split(""));
return new ArrayVector(data.split(""));
}

get length(): number {
return this.#end - this.#start;
}
abstract get length(): number;

at(index: number): T | null {
return this.#data[this.#start + index] ?? null;
abstract at(index: number): T | null;

toString(joinStr = ","): string {
const values = [...this].join(joinStr);
return `[${values}]`;
}

equals(other: Vector<T>): boolean {
Expand All @@ -36,4 +27,92 @@ export class Vector<T> {
}
return true;
}

indexOf(value: T | Vector<T>): number {
if (value instanceof Vector) {
return this.#indexOfVector(value);
}
return this.#indexOfValue(value);
}

contains(value: T | Vector<T>): boolean {
return this.indexOf(value) !== -1;
}

*[Symbol.iterator](): Generator<T> {
for (let i = 0; i < this.length; i++) {
yield this.at(i)!;
}
}

[Symbol.toStringTag]: string = "Vector";

#indexOfValue(value: T): number {
for (let i = 0; i < this.length; i++) {
if (this.at(i) === value) {
return i;
}
}
return -1;
}

#indexOfVector(vector: Vector<T>): number {
if (this.length < vector.length) {
return -1;
}
for (let i = 0; i < this.length - vector.length; i++) {
let found = true;
for (let j = 0; j < vector.length; j++) {
if (this.at(i + j) !== vector.at(j)) {
found = false;
break;
}
}
if (found) {
return i;
}
}
return -1;
}
}

export class ArrayVector<T> extends Vector<T> {
#data: Array<T>;

constructor(data: Array<T>) {
super();
this.#data = data;
}

override get length(): number {
return this.#data.length;
}

override at(index: number): T | null {
return this.#data[index] ?? null;
}
}

export class SubVector<T> extends Vector<T> {
#vector: Vector<T>;
#start: number;
#end: number;

constructor(vector: Vector<T>, start: number, end: number) {
super();
this.#vector = vector;
this.#start = start;
this.#end = end;
}

override get length(): number {
return this.#end - this.#start;
}

override at(index: number): T | null {
if (index < 0 || index >= this.length) {
return null;
}
return this.#vector.at(this.#start + index);
}
}
67 changes: 44 additions & 23 deletions lib/datastructures/vector/vector_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,56 @@ import { expect } from "@std/expect";
import { describe, it } from "@std/testing/bdd";
import { Vector } from "./vector.ts";

const defaultVector = new Vector([1, 2, 3, 4, 5, 6]);
describe("Vector", () => {
it("factory from", () => {
expect(Vector.from([1, 2, 3, 4, 5, 6]).length).toBe(6);
expect(Vector.from([]).length).toBe(0);
expect(Vector.from([{ x: 1, y: 2 }, { x: 3, y: 4 }]).length).toBe(2);
});

describe("Vector::new", () => {
it("should handle arrays", () => {
expect(defaultVector.length).toBe(6);
expect(new Vector([]).length).toBe(0);
expect(new Vector([{ x: 1, y: 2 }, { x: 3, y: 4 }]).length).toBe(2);
it("factory fromStrings", () => {
expect(Vector.fromString("123456").length).toBe(6);
});
});

describe("Vector.fromStrings", () => {
it("should work", () => {
const vector = Vector.fromString("123456");
expect(vector.length).toBe(6);
});
describe("ArrayVector", () => {
vectorImplTestSuite(Vector.from([1, 2, 3, 4, 5, 6]));
});

describe("Vector::at", () => {
it("should return the value at the given index", () => {
expect(defaultVector.at(0)).toBe(1);
expect(defaultVector.at(5)).toBe(6);
expect(defaultVector.at(6)).toBe(null);
expect(defaultVector.at(-1)).toBe(null);
});
describe("SubVector", () => {
});

describe("Vector::equals", () => {
it("should return true if the vectors are equal", () => {
expect(defaultVector.equals(new Vector([1, 2, 3, 4, 5, 6]))).toBe(true);
expect(defaultVector.equals(new Vector([1, 2, 3, 4, 5]))).toBe(false);
export function vectorImplTestSuite(vector: Vector<number>) {
it("method at", () => {
expect(vector.at(0)).toBe(1);
expect(vector.at(5)).toBe(6);
expect(vector.at(6)).toBe(null);
expect(vector.at(-1)).toBe(null);
});
});

it("method toString", () => {
expect(vector.toString()).toBe("[1,2,3,4,5,6]");
expect(Vector.from([]).toString()).toBe("[]");
});

it("method equals", () => {
expect(vector.equals(Vector.from([1, 2, 3, 4, 5, 6]))).toBe(true);
expect(vector.equals(Vector.from([1, 2, 3, 4, 5]))).toBe(false);
});

it("method indexOf", () => {
expect(vector.indexOf(1)).toBe(0);
expect(vector.indexOf(3)).toBe(2);
expect(vector.indexOf(6)).toBe(5);
expect(vector.indexOf(7)).toBe(-1);
expect(vector.indexOf(Vector.from([3, 4, 5]))).toBe(2);
expect(vector.indexOf(Vector.from([3, 4, 7]))).toBe(-1);
expect(vector.indexOf(Vector.from([]))).toBe(0);
expect(vector.indexOf(Vector.from([1, 2, 3, 4, 5, 6, 7]))).toBe(-1);
});

it("method [Symbol.iterator]", () => {
const values = [...vector];
expect(values).toEqual([1, 2, 3, 4, 5, 6]);
});
}

0 comments on commit 691673a

Please sign in to comment.