How can I deal with circular references in MobX? - javascript

I have this code:
var root = {};
var left = {};
rootClass.left = left;
var right = {};
right.left = left;
left.right = right;
var o = observable(root);
right has pointer to left and left has pointer to right.
When last line executed I get this error:
RangeError: Maximum call stack size exceeded
at _tryDefineProperty (vendor.bundle.js:91185)
at Function.Object.defineProperty (vendor.bundle.js:91141)
at addHiddenFinalProp (vendor.bundle.js:14605)
at asObservableObject (vendor.bundle.js:14122)
at Function.IObservableFactories.object (vendor.bundle.js:12329)
at deepEnhancer (vendor.bundle.js:13382)
at new ObservableValue (vendor.bundle.js:14280)
at defineObservableProperty (vendor.bundle.js:14163)
at defineObservablePropertyFromDescriptor (vendor.bundle.js:14143)
at extendObservableHelper (vendor.bundle.js:12207)
at extendObservable (vendor.bundle.js:12178)
at Function.IObservableFactories.object (vendor.bundle.js:12330)
at deepEnhancer (vendor.bundle.js:13382)
at new ObservableValue (vendor.bundle.js:14280)
at defineObservableProperty (vendor.bundle.js:14163)
How can I deal with circular references in MobX?

The idea behind MobX is Excel spreadsheets. You need to think about which data can be derived and which could'nt be derived.
References are a great example of derived data, if your object have a circular structure with keys.
Which you'll end up doing is simply store all the objects keyed by id in a object (or a MobX Map).
Next, instead of creating the left and right properties, create them as computed getters and setters.
Left and right object id will be stored in properties (e.g. leftId and rightid).
The getter will simply resolve the left or right by looking at the leftId in the root object store. The setter will simply provide a logic to store the id in the leftId/rightId property instead of storing the whole object.
observable({
id: nextId(), // progressive id for sample
name,
leftId: null, // variables to store the id in {$ref: 1}
rightId: null,
get left(){ // getters and setters
return allItems.has(this.leftId) ?
allItems.get(this.leftId) :
null
},
set left(item){
this.leftId = item ? item.id : null
},
get right(){
return allItems.has(this.rightId) ?
allItems.get(this.rightId) :
null
},
set right(item){
this.rightId = item ? item.id : null
},
})
Whole Fiddle Example: https://jsfiddle.net/4ure5kak/2/

Related

Calculate Height/Depth Of Binary Tree Javascript

I am just a beginner, so if the error is something too obvious, I apologize .
My two questions are:
What is this.root in our school's provided code;
How can I implement the .height method in order to measure the depth of a Tree.
The explanation:
We were provided with this code in the class:
function BinarySearchTree(value) {
this.value = value;
this.right = null;
this.left = null;
}
BinarySearchTree.prototype.add = function(value) {
let newLeaf = new BinarySearchTree(value)
if(value > this.value){
this.right === null? this.right = newLeaf : this.right.add(value)
} else {
this.left === null? this.left = newLeaf : this.left.add(value)
}
};
And we were supposed to write a method to calculate the height/depth of a binary tree. Now, while practicing, I've seen something odd. Upon creation of a new node of an empty binary tree, the first node ends up being completely empty, while it proceeds to create a new node on the left side of the first empty one. Well, not empty, but whose value is undefined. Is this a desired behavior?
let newTree = new BinarySearchTree
>undefined
newTree.add(7)
>undefined
newTree.add(3)
>undefined
newTree.add(5)
>undefined
newTree
>BinarySearchTree {value: undefined, right: null, left: BinarySearchTree}
left: BinarySearchTree {value: 7, right: null, left: BinarySearchTree}
right: null
value: undefined
[[Prototype]]: Object
Now, considering the tests are passing for .add method, obviously I may be wrong in this situation, since this is the code provided to us by the teacher in the class.
This is the code I keep finding online and the reason I am not getting far with my code for .heigth method is because I am unable to implement this.root:
function Node(val){
this.value = val;
this.left = null;
this.right = null;
}
function BinarySearchTree(){
this.root = null;
}
How should I proceed with the .height method?
If it helps, here are the tests:
describe('Binary Search Tree', function() {
var binarySearchTree;
beforeEach(function() {
binarySearchTree = new BinarySearchTree(5);
});
it('should have methods named "add", "contains", "depthFirstPre", "depthFirstIn", "depthFirstPost", "breadthFirst"', function() {
expect(binarySearchTree.add).to.be.a("function");
});
it('should add values at the correct location in the tree', function(){
binarySearchTree.add(2);
binarySearchTree.add(3);
binarySearchTree.add(7);
binarySearchTree.add(6);
expect(binarySearchTree.left.right.value).to.equal(3);
expect(binarySearchTree.right.left.value).to.equal(6);
});
it('height method should return correct height', function() {
binarySearchTree.left = new BinarySearchTree(3);
binarySearchTree.left.left = new BinarySearchTree(1);
expect(binarySearchTree.height()).to.eql(2);
binarySearchTree.left.left.right = new BinarySearchTree(2);
expect(binarySearchTree.height()).to.eql(3);
binarySearchTree.left.left.left = new BinarySearchTree(0);
expect(binarySearchTree.height()).to.eql(3);
binarySearchTree.right = new BinarySearchTree(8);
expect(binarySearchTree.height()).to.eql(3);
});
}
Again, I apologize for a long question. I was trying to write all the relevant information regarding my problem.
Happy holidays!
What is this.root in our school's provided code
Your school's template code does not manage what is the root of the tree, so this must be managed in a variable by the driver code. In the testing code this variable is named binarySearchTree, and it really is what would be called this.root in the second (2-class) implementation.
Now, while practicing, I've seen something odd. Upon creation of a new node of an empty binary tree, the first node ends up being completely empty [...] Is this a desired behavior?
No it is not desired behavior. The template code does not provide the concept of an empty binary tree. It expects you to create the tree with at least one value, which should be provided as argument to the constructor. It is not intended to leave out the argument when calling the constructor.
The 2-class implementation provides the idea of an empty tree. But the school's template code does not; you would just have to state binarySearchTree = null if you want an empty tree. But the downside is clear: you cannot use the methods of the class to add a value to that. The only way to get the first value in a tree is to call the constructor and assign the constructed object to your binarySearchTree variable. So adding the very first value to the tree requires a different approach than adding the other values. This is also what you see in the testing code: the first value is added as argument to the constructor -- which is always called with an argument -- while the other values are added by calling the add method. This is a pity and really shows the limitations of the template code.
How can I implement the .height method in order to measure the depth of a Tree.
The idea is that you use recursion:
If there is a left child, get the height of the left subtree through recursion. If there is none, use -1 as default, as it is an empty subtree, and empty trees have a height of -1. Do the same at the right side. Get the maximum of these two values, since only the higher subtree of the two determines what is the height of the tree. Finally add one to this result so to account for the current node.
BinarySearchTree.prototype.height = function() {
return 1 + Math.max(
this.left !== null ? this.left.height() : -1,
this.right !== null ? this.right.height() : -1
);
};
Again, you can only run the height method an a tree that has at least one node, because of the limitations of the school's template code.
For completeness sake, the 2-class equivalent would place the above code on the Node class, and would add a wrapper method on the BinarySearchTree class, like this:
Node.prototype.height = function() {
return 1 + Math.max(
this.left !== null ? this.left.height() : -1,
this.right !== null ? this.right.height() : -1
);
};
BinarySearchTree.prototype.height = function() {
return this.root === null ? -1 : root.height();
}

How to avoid multiple try catch for undefined values (by using if/else or some other way) while fetching border values from Google Spreadsheet?

I am fetching border values from the sheet and creating an HTML table in Gmail. I am new to Javascript (google script) and struggling to optimize the code.
I am using advanced sheets property to fetch border values for each cell. If the border doesn't exist, rather than returning "none", the values are undefined. Hence, using try-catch to set the border value to "none".
I read a few blogs which said try-catch should be minimally used and it slows down the code. Is there a way to improve this code as I am using try-catch for each border (top, left, right and bottom) for every cell?
The error returned without try-catch is 'Cannot read property "0.0" from undefined'.
//Creating an array of border values by assigning the range values
var aBorderValues = JSON.parse(JSON.stringify(Sheets.Spreadsheets.get(spreadsheetID, {ranges: borderRange, fields: "sheets/data/rowData/values/userEnteredFormat/borders"})));
// Getting the top border style
try
{
var topBorder =aBorderValues.sheets[0].data[0].rowData[i].values[j].userEnteredFormat.borders.top.style;
}
catch (etop)
{
topBorder = "none";
}
You could write something like get in lodash and provide the path to the property you want to access as an array of strings and/or numbers:
function get (object, path, defaultValue) {
return object === undefined //object at current path is undefined
? defaultValue //return default value
: path.length === 0 //no more items in path
? object //return value at completed path (path.length === 0)
: object[path[0]] === undefined //go one level deeper
? defaultValue //there is nothing one level deeper, return default
: get(object[path[0]], path.slice(1), defaultValue); //recursively call itself
}
const test = [{ name: 'hi' }];
console.log(get(test, [0, 'name']));
console.log(get(test, [1, 'name'], 'default value'));
console.log(
get(undefined, [1, 'name'], 'other default value'),
);
//index of array does not have to be a number:
console.log(get(test, ['0', 'name']));

How do I set a JavaScript object's value to null

I have created this JS object from an array.
var rv = {};
$( ".part-name:visible" ).each(function( index ) {
//rv[$(this).text()] = arrayPartsName[$(this).text()];
rv[$(this).text()] = arrayPartsName[$(this).text()];
console.log(rv);
})
4GN: "4GN"
4GNTS: "4GNTS"
042645-00: "042645-00"
503711-03: "503711-03"
573699-05: "573699-05"
I have to use this object with Materialize Autocomplete and I have to edit it. The correct object must be, for example, like this
4GN: null
4GNTS: null
042645-00: null
503711-03: null
573699-05: null
How can do this?
Picking up from my comment. You can just set it to null ;) JavaScript is quite a cool language... you can pretty much set any object's properties to anything you want, null, a specific value, or even a function... see some more on the topic
But to focus on your specific question:
Change this line
rv[$(this).text()] = arrayPartsName[$(this).text()];
to
rv[$(this).text()] = null;
Something to be aware of
If you have property or key values in the JSON object with a dash in the name, you have to wrap it in quotes ", otherwise it wont be seen as valid. Although this might not be as evident, or an issue in your example as your keys are being added via the following function $(this).text().
var fruit = {
"pear": null, // something null
"talk": function() { console.log('WOOHOO!'); } // function
}
var apple = "app-le";
fruit[apple.toString()] = 'with a dash';
fruit["bana-na"] = 'with a dash';
// below is not allowed, the values will be evaluated as
// properties that dont exist, and then your js will fail
// fruit[pe-ar] = 'with a dash';
fruit.talk();
console.log(fruit);

How can I compare a string to an object key and get that key's value?

I want to do something relatively simple, I think anyways.
I need to compare the pathname of page with an object's kv pairs. For example:
if("pathname" === "key"){return value;}
That's pretty much it. I'm not sure how to do it in either regular Javascript or jQuery. Either are acceptable.
You can see my fiddle here: http://jsfiddle.net/lz430/2rhds1x3/
JavaScript:
var pageID = "/electrical-electronic-tape/c/864";
var pageList = [{
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
}];
var label = pageID in pageList;
$('.el').html(label);
First, your "pageList" should just be a plain object, not an object in an array:
var pageList = {
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
};
Then you can set "label" to the value from the mapping:
var label = pageList[pageID] || "(not found)";
That last bit of the statement above will set the label to "(not found)" if the lookup fails, which may or may not be applicable to your situation.
It depends kinda on the logic you want to implement. If you want to say "if object has the key, then do X, and if not, then do Y", then you handle that differently than "set label to the object's key's value if the key is there, or else set it to undefined or something else".
For the first case you do:
if (pageList.hasOwnProperty(pageID) ) {
label = pageList[pageID];
}
else {
// do whatever, maybe some error?
}
For the second case, you can just say
var label = pageList[pageID] || 'notFound';
As indicated by #Pointy, either get rid of the array or subsiture pageList[0] for pageList and pageList[0][pageID] for pageList[pageID] above, if you need to keep the array.

Pushing a children to "this" will also push it in the child's childrens... (Javascript)

I have some entity/component code in javascript. It's mostly done but I am hitting this really weird problem. My entities have a childrens array in which I push the childrens, and some other array (componentsDictionary, will be renamed don't worry, it used to be a dict) for it's components.
Now when I am calling this.childrens.push(obj), it's pushing the object both in this.childrens and inside obj.childrens... Causing me an infinite loop when I will update my render tree.
Probably a problem with the really weird handling of closures in JS...
Here is the problematic code :
Entity.prototype = {
childrens : [],
componentsDictionary : [],
sharedAttributes : {}, // This data is shared for all components
debugName : "Entity Default Name",
bubblee : null,
Add : function(obj) {
if (obj instanceof Entity) {
alert(obj.debugName); // alerts "entity 0"
alert(this.debugName); // alerts "root"
alert(obj.childrens.length); // "alerts 0"
this.childrens.push(obj);
alert(obj.childrens.length); // "alerts 1"
// how the f... !%!??!%11?
}
else if (obj instanceof IComponent) {
this.componentsDictionary[obj.type].push(obj);
}
else {
throw new Exceptions.IllegalAction("Attempted to add something else than an entity or a component to an entity.");
}
},
Thanks alot!
Nic
Because you've put the "childrens" array on the prototype object, it's shared by every instance of "Entity". There's only one array, in other words.
If you want a separate array per instance, remove it from the prototype and add
this.childrens = [];
to the "Entity" constructor.

Categories

Resources