The reason this was made was because of a combination of things. Firstly, it was made for me to get more acquainted with some ECMA2016 syntax. Secondly, this is a useful utility class when working with grids of undefined width and height, including negative points (I couldn't easily find some already made). Besides that, I also tried abstracting the class in a way, returning a function, I think would result in a convenient shorthand way of using the class.
Any pointers would be appreciated. If there's something I could use / use better with the new ECMA2016 features, that'd be awesome to see!
I'm betting using get
and set
as function names in the class isn't such a great convention to follow, either.
For some reason, only parts of the code are pasted and reformatted weird online. Otherwise, it looks correct in Atom.
/*
A grid which can have any bounds and be checked against.
create: let grid = new BoundlessGrid();
set: grid( 0,0, "start point" )
get: let myVal = grid(0,0);
delete y: delete grid()[0][0];
delete x: delete grid()[0];
iterate: grid().forEach( (x,y,grid)=> {... } );
*/
function forEach(callback) {
for (let x in this) {
for (let y in this[x]) {
callback(x, y, this);
}
}
};
class BoundlessGrid {
constructor() {
this.grid = { forEach: forEach };
Object.defineProperty(this.grid, "forEach", { enumerable: "false" });
// return a function that abstracts the object into
// something that has friendlier shorthand syntax
return (x, y, val) => {return this.handler(x, y, val)};
}
set(x, y, val) {
if (!this.grid.hasOwnProperty(x)) {
let self = this;
// automatically removes the x grid axis when
// the last y point is deleted from that x
this.grid[x] = new Proxy({}, {
deleteProperty(yAxis, y) {
delete yAxis[y];
if (Object.keys(yAxis).length == 0) { delete self.grid[x];}
}
});
}
this.grid[x][y] = val;
}
get(x, y) { if (this.grid.hasOwnProperty(x)) { return this.grid[x][y]; } }
// Single function that delegates to others
// by determining inputs
handler(x, y, val) {
if (x === undefined) { return this.grid; }
if (val !== undefined ) {this.set(x, y, val); }
else { return this.get(x, y); }
}
}
module.exports = BoundlessGrid;
1 Answer 1
An example of some alternatives that you could use, there may be things there that you prefer.
/*
A grid which can have any bounds and be checked against.
create: let grid = new BoundlessGrid();
set: grid( 0,0, "start point" )
get: let myVal = grid(0,0);
delete y: delete grid()[0][0];
delete x: delete grid()[0];
iterate: grid().forEach( (x,y,grid)=> {... } );
*/
function* generateValues(object) {
for (let value in object) {
yield value;
}
}
class BoundlessGrid {
constructor() {
this.grid = Object.create(null, {
forEach: {
value: callback => {
for (let x of generateValues(this)) {
for (let y of generateValues(Reflect.get(this, x))) {
callback(x, y, this);
}
}
},
enumerable: false,
writable: false
}
});
this.deleteProperty = Reflect.deleteProperty.bind(null, this.grid);
// return a function that abstracts the object into
// something that has friendlier shorthand syntax
return (x, y, val) => this.handler(x, y, val);
}
has(x) {
return Reflect.has(this.grid, x);
}
set(x, y, val) {
if (!this.has(x)) {
const deleteProperty = this.deleteProperty;
// automatically removes the x grid axis when
// the last y point is deleted from that x
Reflect.set(this.grid, x, Reflect.construct(Proxy, [Object.create(null), {
deleteProperty(yAxis, y) {
Reflect.deleteProperty(yAxis, y);
if (Object.keys(yAxis).length === 0) {
deleteProperty(x);
}
}
}]));
}
Reflect.set(Reflect.get(this.grid, x), y, val);
}
get(x, y) {
if (this.has(x)) {
return Reflect.get(Reflect.get(this.grid, x), y);
}
}
// Single function that delegates to others
// by determining inputs
handler(x, y, val) {
if (x === undefined) {
return this.grid;
}
return val === undefined ? this.get(x, y) : this.set(x, y, val);
}
}
const grid = Reflect.construct(BoundlessGrid, []);
grid(5, 4, "test1");
grid(5, 3, "test2");
grid().forEach((x, y, g) => console.log(`loop: val of ${x} and ${y}`, g[x][y]));
delete grid()[5][4];
console.log("First deleted key at 5x 4y:", grid(5, 4));
console.log("Value at 5x 3y:", grid(5, 3));
console.log("array of 5x", grid()[5]);
delete grid()[5][3];
console.log("second deleted key exists?", grid(5, 3) !== undefined);
console.log("array 5x defined?", grid()[5]);
Object.create(null)
forthis.grid
so that you don't pick up any unwanted properties. But you would need to change yourhasOwnProperty
checks.const hop = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
\$\endgroup\$forEach
read-only,writable: false
then it can be broken viaset
.Object.create(null)
would also be used fory
inProxy
. SetforEach
viadefineProperty
asvalue
and move the function directly there so it is understood whatthis
is referring too.let self = this;
seems unnecessary. \$\endgroup\$Reflect
methods. \$\endgroup\$