1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346 |
x12
x12
x12
x12
x12
x12
x12
x12
x12
x59
x81
x81
x81
x81
x132
x132
x101
x59
x84
x84
x84
x84
x84
x252
x84
x84
x84
x59
x12
x12
x12
x12
x12
x12
x21
x21
x21
x21
x21
x21
x37
x37
x37
x28
x21
x12
x19
x19
x19
x20
x19
x25
x25
x29
x29
x25
x25
x26
x29
x29
x26
x26
x28
x28
x28
x26
x25
x25
x75
x25
x25
x25
x19
x12
x19
x22
x22
x19
x33
x33
x33
x67
x67
x33
x19
x12
x26
x26
x12
x26
x26
x24
x16
x16
x12 |
I
|
// Copyright 2018-2025 the Deno authors. MIT license.
import { assert } from "@std/assert";
/**
* A 2d array. Unlike a normal array, it does not grow dynamically,
* and needs to be manually resized with the {@linkcode D2Array.prototype.resize | `resize`} method.
* It can never have the width or height be 0.
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(3, 3, false);
* arr.raw[0]![0] = true; // set the top left to true
* const slice = arr.slice(0, 0, 2, 2);
* assertEquals(slice.width, 2);
* assertEquals(slice.height, 2);
* assertEquals(arr.raw[0]![0], true);
*
* arr.insert(0, 0, new D2Array<boolean>(2, 2, true)); // set all values from 0,0 to 1,1 to true
*
* for (const row of arr) {
* console.log(row);
* }
* ```
*
* @typeparam T The type of the values stored in the 2d array.
*/
export class D2Array<T> implements Iterable<T[]> {
/** The initial value used when initializing or resizing the array
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(3, 3, false);
* assertEquals(arr.initialValue, false);
* ```
*/
initialValue: T;
/** The raw underlying value
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(3, 3, false);
* assertEquals(arr.raw, [
* [false, false, false],
* [false, false, false],
* [false, false, false],
* ]);
* ```
*/
raw: T[][];
/**
* Create a new blank 2d array with the provided value as init value
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(3, 3, false);
* assertEquals(arr.raw, [
* [false, false, false],
* [false, false, false],
* [false, false, false],
* ]);
* ```
*
* @param width The width of the 2d array
* @param height The height of the 2d array
* @param initialValue The value to use to initialize the 2d array, also used when resizing
*/
constructor(width: number, height: number, initialValue: T);
/**
* Create a new 2d array from an existing array
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>([
* [false, false, false],
* [false, false, false],
* [false, false, false],
* ], false);
* assertEquals(arr.raw, [
* [false, false, false],
* [false, false, false],
* [false, false, false],
* ]);
* ```
*
* @param value The array to use
* @param initialValue The value to use when resizing
*/
constructor(value: readonly T[][], initialValue: T);
/** implementation
* TODO(kt3k): Remove this jsdoc when the issue below is resolved.
* https://github.com/denoland/deno/issues/30037
*
* @param widthOrValue The width of the 2d array, or the array to use
* @param heightOrInitialValue The height of the 2d array, or the initial value to use
* @param initialValue The initial value to use when resizing the
*/
constructor(
widthOrValue: number | (readonly T[][]),
heightOrInitialValue: number | T,
initialValue?: T,
) {
if (Array.isArray(widthOrValue)) {
assert((widthOrValue.length) > 0, "Height must be greater than 0");
assert((widthOrValue[0].length) > 0, "Width must be greater than 0");
this.raw = [];
for (let i = 0; i < widthOrValue.length; i++) {
this.raw.push(widthOrValue[i]!.slice());
}
this.initialValue = heightOrInitialValue as T;
} else {
assert((widthOrValue as number) > 0, "Width must be greater than 0");
assert(
(heightOrInitialValue as number) > 0,
"Height must be greater than 0",
);
this.raw = Array.from(
{ length: heightOrInitialValue as number },
() => Array(widthOrValue as number).fill(initialValue as number),
);
this.initialValue = initialValue!;
}
}
/**
* Slice the 2D array. Like slice on a normal array, this will create a new D2Array.
* If no arguments are specified, it will slice the entire D2Array.
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(3, 3, false);
* arr.raw[0]![0] = true; // set the top left to true
* const slice = arr.slice(0, 0, 2, 2);
* assertEquals(slice.width, 2);
* assertEquals(slice.height, 2);
* assertEquals(arr.raw[0]![0], true);
* ```
*
* @param x 0-based index at which to start on the X axis (0 is left-most)
* @param y 0-based index at which to start on the Y axis (0 is top-most)
* @param width the amount of elements to take on the X axis
* @param height the amount of elements to take on the X axis
* @returns A new {@linkcode D2Array} containing the sliced values.
*/
slice(
x: number = 0,
y: number = 0,
width?: number,
height?: number,
): D2Array<T> {
const actualHeight = Math.min(height ?? Infinity, this.raw.length - y);
const actualWidth = Math.min(width ?? Infinity, this.raw[0]!.length - x);
assert((actualWidth as number) > 0, "Width must be greater than 0");
assert((actualHeight as number) > 0, "Height must be greater than 0");
const out: T[][] = [];
for (let i = y; i < y + actualHeight; i++) {
const row = this.raw[i]!;
out.push(row.slice(x, x + actualWidth));
}
return new D2Array(out, this.initialValue);
}
/**
* Resize the 2D array.
* The origin for the resize is the top left corner, or rather the first element.
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(3, 3, false);
* arr.raw[0]![0] = true; // set the top left to true
* arr.resize(2, 2);
* assertEquals(arr.width, 2);
* assertEquals(arr.height, 2);
* assertEquals(arr.raw[0]![0], true);
* ```
*
* @param width the new width of the 2d array.
* If smaller than current width, the 2d array will lose items.
* If larger than current width, the 2d array will add instances of {@linkcode D2Array.prototype.initialValue | `initialValue`} to fill up.
* @param height the new width of the 2d array.
* If smaller than current height, the 2d array will lose items.
* If larger than current height, the 2d array will add instances of {@linkcode D2Array.prototype.initialValue | `initialValue`} to fill up.
*/
resize(width: number, height: number) {
assert((width as number) > 0, "Width must be greater than 0");
assert((height as number) > 0, "Height must be greater than 0");
if (height == this.raw.length && width == this.raw[0]!.length) {
return;
} else if (height < this.raw.length && width < this.raw[0]!.length) {
this.raw = this.raw.slice(0, height);
for (let i = 0; i < this.raw.length; i++) {
this.raw[i] = this.raw[i]!.slice(0, width);
}
} else {
if (width <= this.raw[0]!.length) {
for (let i = 0; i < this.raw.length; i++) {
this.raw[i] = this.raw[i]!.slice(0, width);
}
} else {
for (let i = 0; i < this.raw.length; i++) {
this.raw[i]!.push(
...Array(width - this.raw[i]!.length).fill(this.initialValue),
);
}
}
if (height <= this.raw.length) {
this.raw = this.raw.slice(0, height);
} else {
this.raw.push(
...Array.from(
{ length: height - this.raw.length },
() => Array(this.raw[0]!.length).fill(this.initialValue),
),
);
}
}
}
/**
* Insert another 2d array at specific coordinates.
* If the inserted 2d array is greater than the 2d array this method is called on,
* the inserted value will be trimmed to fit the current 2d array.
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(5, 5, false);
* arr.insert(2, 2, new D2Array<boolean>(3, 3, true));
* assertEquals(arr.raw, [
* [false, false, false, false, false],
* [false, false, false, false, false],
* [false, false, true, true, true],
* [false, false, true, true, true],
* [false, false, true, true, true],
* ]);
* ```
*
* @param x The X coordinate to place the 2d array, 0-based.
* @param y The Y coordinate to place the 2d array, 0-based.
* @param value The 2d array to insert at the provided coordinates.
*/
insert(x: number, y: number, value: readonly T[][] | D2Array<T>) {
if (value instanceof D2Array) {
value = value.raw;
}
for (let i = y; i < Math.min(this.raw.length, y + value.length); i++) {
const thisRow = this.raw[i]!;
const valueRow = value[i - y]!;
for (let j = x; j < Math.min(thisRow.length, x + valueRow.length); j++) {
thisRow[j] = valueRow[j - x]!;
}
}
}
/**
* The width of the 2d array.
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(3, 4, false);
* assertEquals(arr.width, 3);
* ```
*
* @returns The width of the 2d array.
*/
get width(): number {
return this.raw[0]!.length;
}
/**
* The height of the 2d array.
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
*
* const arr = new D2Array<boolean>(3, 4, false);
* assertEquals(arr.height, 4);
* ```
*
* @returns The height of the 2d array.
*/
get height(): number {
return this.raw.length;
}
/**
* Iterate over the underlying raw array
*
* @example Usage
* ```ts
* import { D2Array } from "@std/data-structures/unstable-2d-array";
* import { assertEquals } from "@std/assert";
* const arr = new D2Array<boolean>(3, 3, false);
*
* for (const row of arr) {
* assertEquals(row.length, 3);
* }
* ```
*
* @returns An iterator over the rows of the 2d array.
*/
[Symbol.iterator](): Iterator<T[]> {
return this.raw[Symbol.iterator]();
}
}
|