Skip to content

Commit ee75d23

Browse files
author
FiddlyDigital
committed
Added ability to iterate and some related tests
1 parent 0ff7366 commit ee75d23

3 files changed

Lines changed: 123 additions & 1 deletion

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@speakingsoftware/fastmap",
3-
"version": "1.1.2",
3+
"version": "1.2.0",
44
"description": "A 2d Array optimized for speed and memory usage.",
55
"main": "./dist/index.js",
66
"module": "./dist/index.mjs",

src/index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,48 @@ export class FastMap<T> {
145145
throw new Error("Specified dimensions are too large");
146146
}
147147
}
148+
149+
/**
150+
* Iterate over entries in row-major order.
151+
* Each entry is a tuple: [[x, y], value]
152+
* @example [for (const [[x, y], value] of map) { ... }]
153+
*/
154+
public *entries(): IterableIterator<[[number, number], T | undefined]> {
155+
for (let y = 0; y < this.height; y++) {
156+
for (let x = 0; x < this.width; x++) {
157+
yield [[x, y], this.map[this.convert2DTo1D(x, y)]];
158+
}
159+
}
160+
}
161+
162+
/**
163+
* Iterate over keys as [x, y] tuples in row-major order.
164+
* @example [for (const [x, y] of map.keys()) { ... }]
165+
*/
166+
public *keys(): IterableIterator<[number, number]> {
167+
for (let y = 0; y < this.height; y++) {
168+
for (let x = 0; x < this.width; x++) {
169+
yield [x, y];
170+
}
171+
}
172+
}
173+
174+
/**
175+
* Iterate over values in row-major order.
176+
* @example [for (const value of map.values()) { ... }]
177+
*/
178+
public *values(): IterableIterator<T | undefined> {
179+
for (let y = 0; y < this.height; y++) {
180+
for (let x = 0; x < this.width; x++) {
181+
yield this.map[this.convert2DTo1D(x, y)];
182+
}
183+
}
184+
}
185+
186+
/**
187+
* Default iterator - same as `entries()` so constructs like `for (const [key, value] of map)` work.
188+
*/
189+
public [Symbol.iterator](): IterableIterator<[[number, number], T | undefined]> {
190+
return this.entries();
191+
}
148192
}

tests/iteration.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { FastMap } from '../src/index';
2+
3+
describe('FastMap iteration', () => {
4+
it('entries() yields [[x,y], value] in row-major order', () => {
5+
const width = 3;
6+
const height = 2;
7+
const map = new FastMap<number>(width, height);
8+
9+
// set a few values, leave others undefined
10+
map.Set(0, 0, 1);
11+
map.Set(2, 0, 2);
12+
map.Set(1, 1, 3);
13+
14+
const entries = Array.from(map.entries());
15+
expect(entries.length).toBe(width * height);
16+
17+
// each entry should match map.Get(x,y)
18+
for (const [[x, y], value] of entries) {
19+
expect(map.Get(x, y)).toBe(value);
20+
}
21+
22+
// check first and last coordinates
23+
expect(entries[0][0]).toEqual([0, 0]);
24+
expect(entries[0][1]).toBe(1);
25+
expect(entries[entries.length - 1][0]).toEqual([2, 1]);
26+
});
27+
28+
it('keys() yields coordinates in row-major order', () => {
29+
const width = 4;
30+
const height = 3;
31+
const map = new FastMap<number>(width, height);
32+
33+
const keys = Array.from(map.keys());
34+
expect(keys.length).toBe(width * height);
35+
36+
let idx = 0;
37+
for (let y = 0; y < height; y++) {
38+
for (let x = 0; x < width; x++) {
39+
expect(keys[idx]).toEqual([x, y]);
40+
idx++;
41+
}
42+
}
43+
});
44+
45+
it('values() yields values in row-major order', () => {
46+
const width = 5;
47+
const height = 2;
48+
const map = new FastMap<number>(width, height);
49+
50+
// initialize map with predictable values: value = y * width + x
51+
for (let y = 0; y < height; y++) {
52+
for (let x = 0; x < width; x++) {
53+
map.Set(x, y, y * width + x);
54+
}
55+
}
56+
57+
const values = Array.from(map.values());
58+
expect(values.length).toBe(width * height);
59+
60+
for (let i = 0; i < values.length; i++) {
61+
expect(values[i]).toBe(i);
62+
}
63+
});
64+
65+
it('default iterator yields same as entries()', () => {
66+
const width = 3;
67+
const height = 3;
68+
const map = new FastMap<string>(width, height);
69+
70+
map.Set(0, 0, 'a');
71+
map.Set(2, 2, 'z');
72+
73+
// Avoid using `for..of` (requires downlevelIteration for es5 target).
74+
const fromDefault = Array.from((map as any)[Symbol.iterator]() as Iterable<[[number, number], string | undefined]>);
75+
76+
expect(fromDefault).toEqual(Array.from(map.entries()));
77+
});
78+
});

0 commit comments

Comments
 (0)