1
\$\begingroup\$

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.

GitHub
JsFiddle demonstration

/*
 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;
asked Sep 21, 2016 at 22:26
\$\endgroup\$
5
  • \$\begingroup\$ Any example data/use? \$\endgroup\$ Commented Sep 21, 2016 at 22:53
  • \$\begingroup\$ @Xotic750 how to use the functions, or do basic operations with it are commented at the top of the code. I will link a JsFiddle of it in action in a second here. \$\endgroup\$ Commented Sep 21, 2016 at 23:00
  • 1
    \$\begingroup\$ Perhaps Object.create(null) for this.grid so that you don't pick up any unwanted properties. But you would need to change your hasOwnProperty checks. const hop = Function.prototype.call.bind(Object.prototype.hasOwnProperty); \$\endgroup\$ Commented Sep 22, 2016 at 0:16
  • \$\begingroup\$ You could make forEach read-only, writable: false then it can be broken via set. Object.create(null) would also be used for y in Proxy. Set forEach via defineProperty as value and move the function directly there so it is understood what this is referring too. let self = this; seems unnecessary. \$\endgroup\$ Commented Sep 22, 2016 at 6:26
  • \$\begingroup\$ You can also use Reflect methods. \$\endgroup\$ Commented Sep 22, 2016 at 7:26

1 Answer 1

1
\$\begingroup\$

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]);

answered Sep 22, 2016 at 8:20
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.