Skip to content

Commit a123989

Browse files
authored
Merge pull request #3 from neefrehman/chore/refactor-1
feature: self-aware vector population
2 parents 0607056 + f71b6c4 commit a123989

File tree

8 files changed

+295
-60
lines changed

8 files changed

+295
-60
lines changed

.prettierrc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
2-
"printWidth": 80,
3-
"tabWidth": 4,
4-
"singleQuote": false,
5-
"trailingComma": "none",
6-
"bracketSpacing": true,
7-
"arrowParens": "avoid",
8-
"semi": true
2+
"printWidth": 82,
3+
"tabWidth": 4,
4+
"singleQuote": false,
5+
"trailingComma": "none",
6+
"bracketSpacing": true,
7+
"arrowParens": "avoid",
8+
"semi": true
99
}

.vscode/settings.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

README.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,24 @@ const threeDimensionalStringArray = makeMatrix([2, 6, 5], "value");
7272

7373
### Pass a callback for dynamic initial values
7474

75-
The `initialValues` parameter also accepts a callback, which you can use to dynamically create items for each position in the matrix.
75+
The `initialValues` parameter can also be a callback, which you can use to dynamically create items for each position in the matrix. The callback can accept an argument which will resolve to the current vector co-ordinates at each point in the matrix. This can allow you to populate your matrix in a "self-aware" way.
7676

7777
```js
78-
// create a 10x10x10 array, with each point a different random number between one and 10
78+
// create a 10x10x10 array, with each point a different random number between one and 9
7979
const twoDRandomNumberArray = makeMatrix([10, 10, 10], () => Math.floor(Math.random() * 10));
80+
81+
// create a 5x5 array, with each point self described by a string
82+
const twoDRandomNumberArray = makeMatrix([5, 5], (vector) => vector.join());
83+
84+
// create a 7x3,8 array, with each point transformed into a vector object
85+
const twoDRandomNumberArray = makeMatrix([7, 3, 8], (vector) => {
86+
return {
87+
x: vector.x,
88+
y: vector.y,
89+
z: vector.z,
90+
otherData: OTHER_DATA
91+
}
92+
});
8093
```
8194

8295
## With TypeScript
@@ -85,7 +98,7 @@ This package comes with type definitions to provide type-safety when working wit
8598

8699
Up to and including four dimensions, returned arrays will be given a specific type dictated by `T` and the number of dimensions, where `T` is the type of the value passed to the functions `initialValues` parameter (or `unknown` if not set).
87100

88-
For example, a three-dimensional array of numbers will be of type `number[][][]`. Points within the matrix can then only be reassigned to numbers. TypeScript's compiler will present errors when any reassignments are of the wrong type, including if they are at an incorrect depth in the array.
101+
For example, a three-dimensional array of numbers will be of type `number[][][]`. Points within the matrix can then only be reassigned to numbers. TypeScript's compiler will present errors when any reassignments are of the wrong type, including if they are at an incorrect depth in the array. This type-safety also exists when callbacks are used to dynamically populate your matrix.
89102

90103
```ts
91104
const threeDNumberArray = makeMatrix([2, 6, 5], 0); // return type of number[][][]
@@ -104,7 +117,7 @@ Above four dimensions, the returned array will have the generic type `Matrix<T>`
104117
For more type-safety at these levels of dimensionality, it is recomended that you add more specific type annotations to your variables:
105118

106119
```ts
107-
const sixDimensionalNumberArray: number[][][][][][] = makeMatrix(6, 0);
120+
const sixDimensionalNumberArray: string[][][][][][] = makeMatrix(6, "this is deep");
108121
```
109122

110123
## Example
@@ -133,9 +146,9 @@ for (let x = 0; x < matrix.length; x += res) {
133146
makeMatrix(dimensions, initialValues);
134147

135148
// With types
136-
makeMatrix<T>(
149+
makeMatrix<D, T>(
137150
dimensions: number | number[],
138-
initialValues?: T | (() => T)
151+
initialValues?: (vector: VectorOfLength<D>) => T
139152
): Matrix<T>;
140153
```
141154

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import makeMatrix from "../index";
2+
3+
// Test arrays of specific dimensions, with callbacks returning "self aware" initialised values
4+
test("vector callback test - straight", () => {
5+
expect(makeMatrix([2, 2, 2], vector => vector)).toStrictEqual([
6+
[
7+
[
8+
[0, 0, 0],
9+
[0, 0, 1]
10+
],
11+
[
12+
[0, 1, 0],
13+
[0, 1, 1]
14+
]
15+
],
16+
[
17+
[
18+
[1, 0, 0],
19+
[1, 0, 1]
20+
],
21+
[
22+
[1, 1, 0],
23+
[1, 1, 1]
24+
]
25+
]
26+
]);
27+
});
28+
29+
test("vector callback test - joined", () => {
30+
expect(
31+
makeMatrix([2, 2, 2, 1], vector => `my position is ${vector.join()}`)
32+
).toStrictEqual([
33+
[
34+
[["my position is 0,0,0,0"], ["my position is 0,0,1,0"]],
35+
[["my position is 0,1,0,0"], ["my position is 0,1,1,0"]]
36+
],
37+
[
38+
[["my position is 1,0,0,0"], ["my position is 1,0,1,0"]],
39+
[["my position is 1,1,0,0"], ["my position is 1,1,1,0"]]
40+
]
41+
]);
42+
});
43+
44+
test("vector callback test - joined", () => {
45+
expect(
46+
makeMatrix([3, 3, 3], ([x, y, z]) => {
47+
return {
48+
x,
49+
y,
50+
z
51+
};
52+
})
53+
).toStrictEqual([
54+
[
55+
[
56+
{ x: 0, y: 0, z: 0 },
57+
{ x: 0, y: 0, z: 1 },
58+
{ x: 0, y: 0, z: 2 }
59+
],
60+
[
61+
{ x: 0, y: 1, z: 0 },
62+
{ x: 0, y: 1, z: 1 },
63+
{ x: 0, y: 1, z: 2 }
64+
],
65+
[
66+
{ x: 0, y: 2, z: 0 },
67+
{ x: 0, y: 2, z: 1 },
68+
{ x: 0, y: 2, z: 2 }
69+
]
70+
],
71+
[
72+
[
73+
{ x: 1, y: 0, z: 0 },
74+
{ x: 1, y: 0, z: 1 },
75+
{ x: 1, y: 0, z: 2 }
76+
],
77+
[
78+
{ x: 1, y: 1, z: 0 },
79+
{ x: 1, y: 1, z: 1 },
80+
{ x: 1, y: 1, z: 2 }
81+
],
82+
[
83+
{ x: 1, y: 2, z: 0 },
84+
{ x: 1, y: 2, z: 1 },
85+
{ x: 1, y: 2, z: 2 }
86+
]
87+
],
88+
[
89+
[
90+
{ x: 2, y: 0, z: 0 },
91+
{ x: 2, y: 0, z: 1 },
92+
{ x: 2, y: 0, z: 2 }
93+
],
94+
[
95+
{ x: 2, y: 1, z: 0 },
96+
{ x: 2, y: 1, z: 1 },
97+
{ x: 2, y: 1, z: 2 }
98+
],
99+
[
100+
{ x: 2, y: 2, z: 0 },
101+
{ x: 2, y: 2, z: 1 },
102+
{ x: 2, y: 2, z: 2 }
103+
]
104+
]
105+
]);
106+
});

src/__tests__/non-integer-error.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,3 @@ import makeMatrix from "../index";
44
test("Array error test - When non-integer is used", () => {
55
expect(() => makeMatrix([1, 0.2, 3])).toThrow(TypeError);
66
});
7-
8-
test("Number error test - When non-integer is used", () => {
9-
expect(() => makeMatrix(0.2)).toThrow(TypeError);
10-
});

src/index.ts

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,4 @@
1-
/**
2-
* A multidimensional array returned by `makeMatrix` for over four dimensions.
3-
*/
4-
export type Matrix<T> = T[][][][][] | Matrix<T>[];
5-
6-
/**
7-
* Provides type safety for values as well as functions that return values
8-
* for `createMatrix`'s `initialValues` parameter.
9-
*/
10-
type ValueOrFunction<T> = T extends boolean
11-
? boolean | (() => boolean) // To solve boolean expanding: https://github.com/microsoft/TypeScript/issues/30029
12-
: T extends any // from: https://github.com/microsoft/TypeScript/issues/37663
13-
? T | (() => T)
14-
: never;
1+
import type { Matrix, ValueOrFunction, Vector } from "./types";
152

163
/**
174
* Returns a matrix (multi-dimensional array) with your desired dimensions and
@@ -36,68 +23,82 @@ type ValueOrFunction<T> = T extends boolean
3623
*
3724
* const twoDRandomNumberArray = makeMatrix([1, 4], () => Math.random()); // A 1x4 array initialised with random numbers
3825
*/
39-
function makeMatrix<T>(
26+
function makeMatrix<D extends number, T>(
4027
dimensions: 1 | [number],
41-
initialValues?: ValueOrFunction<T> | null
28+
initialValues?: ValueOrFunction<D, T>
4229
): T[];
4330

44-
function makeMatrix<T>(
31+
function makeMatrix<D extends number, T>(
4532
dimensions: 2 | [number, number],
46-
initialValues?: ValueOrFunction<T> | null
33+
initialValues?: ValueOrFunction<D, T>
4734
): T[][];
4835

49-
function makeMatrix<T>(
36+
function makeMatrix<D extends number, T>(
5037
dimensions: 3 | [number, number, number],
51-
initialValues?: ValueOrFunction<T> | null
38+
initialValues?: ValueOrFunction<D, T>
5239
): T[][][];
5340

54-
function makeMatrix<T>(
41+
function makeMatrix<D extends number, T>(
5542
dimensions: 4 | [number, number, number, number],
56-
initialValues?: ValueOrFunction<T> | null
43+
initialValues?: ValueOrFunction<D, T>
5744
): T[][][][];
5845

59-
function makeMatrix<T>(
46+
function makeMatrix<D extends number, T>(
6047
dimensions: number | number[],
61-
initialValues?: ValueOrFunction<T> | null
48+
initialValues?: ValueOrFunction<D, T>
6249
): Matrix<T>;
6350

64-
function makeMatrix<T>(
51+
function makeMatrix<D extends number, T>(
6552
dimensions: number | number[],
66-
initialValues: ValueOrFunction<T> | null = null
53+
initialValues: ValueOrFunction<D, T> = null
54+
): Matrix<T> {
55+
const dimensionCount =
56+
typeof dimensions === "number" ? dimensions : dimensions.length;
57+
const intialPosition = Array(dimensionCount).fill(0) as Vector<D>;
58+
59+
return _makeMatrix(dimensions, initialValues, intialPosition) as Matrix<T>;
60+
}
61+
62+
function _makeMatrix<D extends number, T>(
63+
dimensions: number | number[],
64+
initialValues: ValueOrFunction<D, T> = null,
65+
currentPosition: Vector<D>
6766
): Matrix<T> {
6867
let currentDimensionLength: number;
69-
let remainingDimensions: number | number[];
68+
let remainingDimensions: number | Vector;
69+
let remainingDimensionCount: number;
7070
let needsRecursion: boolean;
7171

7272
if (typeof dimensions === "number") {
7373
currentDimensionLength = dimensions;
7474
remainingDimensions = dimensions - 1;
75+
remainingDimensionCount = remainingDimensions;
7576
needsRecursion = remainingDimensions > 0;
7677
} else {
7778
currentDimensionLength = dimensions[0];
7879
remainingDimensions = dimensions.slice(1);
80+
remainingDimensionCount = remainingDimensions.length;
7981
needsRecursion = remainingDimensions.length > 0;
8082
}
8183

8284
if (!Number.isInteger(currentDimensionLength)) {
8385
throw new TypeError(`Dimensions must be integers`);
8486
}
8587

86-
// Spread operator used as as constructed array's can't be mapped:
87-
// https://itnext.io/heres-why-mapping-a-constructed-array-doesn-t-work-in-javascript-f1195138615a
88-
const currentMatrix = [...Array(currentDimensionLength)];
88+
const currentDimension = currentPosition.length - 1 - remainingDimensionCount;
8989

90-
const finalMatrix = needsRecursion
91-
? currentMatrix.map(() =>
92-
makeMatrix(remainingDimensions, initialValues)
93-
)
94-
: currentMatrix.map(() => {
95-
return typeof initialValues === "function"
96-
? initialValues()
97-
: initialValues;
98-
});
90+
const finalMatrix = [...Array(currentDimensionLength)].map((_, i) => {
91+
currentPosition[currentDimension] = i;
92+
return needsRecursion
93+
? _makeMatrix(remainingDimensions, initialValues, currentPosition)
94+
: typeof initialValues === "function" // @ts-expect-error: "expression is not callable" https://github.com/microsoft/TypeScript/issues/37663
95+
? initialValues(currentPosition.slice())
96+
: initialValues;
97+
});
9998

100-
return finalMatrix;
99+
return finalMatrix as Matrix<T>;
101100
}
102101

103102
export default makeMatrix;
103+
104+
export type { Matrix };

0 commit comments

Comments
 (0)