Calculate Height/Depth Of Binary Tree Javascript - 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();
}

Related

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

Phaser: Instantiate class by string reference

I am working on a "manager" for selecting what crop shall be placed on a certain plot. Every crop has a completely different design and therefor their own class/object. However instead of writting >40 different lines that will instantiate that class I would like 1 line that simply contains the string that matches the exact name of a class and then run it. That way my code will stay clean. I have tried some stuff but never managed to get it done. Usually resulting in an the following error:
TypeError: this.crop is not a constructor
The code I am attempting to run
export default class CropManager extends Phaser.Group {
constructor (game, className, plotId) {
super(game)
this.x = 0
this.y = 0
this.plotId = plotId
this.className = className
this.cropHandler(this.className)
}
// Defines which class to call
cropHandler (className) {
const ActualClass = 'plot' + className
this.cropclasses = { ActualClass: ActualClass}
this.crop = this.cropclasses[ActualClass]
this.classRun = new this.crop(this.game, this.x, this.y, this.plotId)
this.add(this.classRun)
}
}
Note every crop their classname = crop+cropname (cropCarrots, cropCows, etc)
Rethink the way you're storing key-value pairs in this.cropclasses. The way it's done now, it's going to have 'ActualClass' as the key and 'plotNameOfTheClass' (or whatever 'plot' + className produces) as the value, thus, when accessing it later as an array, this.crop comes out undefined since there isn't a 'plotNameOfTheClass' key in the map.

Adding meta data to a primitive in javascript

Background
We have much of our data formatted like
var X = {value:'some val',error:'maybe an error',valid:true}
as a result we find ourselves calling X.value ALL the time.
We don't use the .error or .valid nearly as much, but we do use it.
What I want
To quit calling .value everywhere, but to still have access to meta data on a per data point level.
The Question
Is there one of
A) A way to put meta data on a primitive? attaching .error to an int for example? Is it possible for bools or strings?
B) A way to make a class that can be treated as a primitive, providing a specific data member when I do? IE X.value = 5, X+3 returns 8.
C) A better design for our data? Did we just lay this out wrong somehow?
You can set the method toString() to your object and return value.
var X = {
value: 1,
error:'maybe an error',
valid:true,
toString: function() {
return this.value;
}
}
X.value = 5;
console.log(X+3);
You can represent you data as a function object that also has properties:
var X = () => 1;
X.value = 1;
X.error = 'maybe an error';
X.valid = true,
console.log(X()); // 1
console.log(X.valid); // true
For better design you can encapsulate the creation of the data object in another function.

How can I deal with circular references in MobX?

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/

Ember store adding attributes incorrectly

I'm using the latest version of ember-cli, ember-data, ember-localstorage-adapter, and ember.
I have a Node object which has a parent and children. Since I had issues with creating multiple relationships with the same type of object, I decided to store the parentID in a string, and the childIDs in an array of strings. However, when I create a new Node and try to add the new Node's to the parents array of IDs, the ID ends up being added to the correct parent, but also other parents.
level 1 0
/ \
level 2 1 2
| |
level 3 3 4
In a structure like this, 0, 1, and 2 all have correct child and parent IDs. However, after adding 3 and 4, node 1 and node 2's childIDs are [3, 4], instead of [3], [4] respectively.
The Array attribute:
var ArrayTransform = DS.Transform.extend({
serialize: function(value) {
if (!value) {
return [];
}
return value;
},
deserialize: function(value) {
if (!value) {
return [];
}
return value;
}
});
The insertNode code:
insert: function(elem) {
var i,
_store = elem.node.store,
newNodeJSON = elem.node.serialize();
newNodeJSON.childIds = [];
newNodeJSON.level = getNextLevel();
_store.filter('node', function(node) {
return node.get('level') === newnodeJSON.level-1;
}).then(function(prevLevelNodes) {
// if no other nodes yet
if (prevLevelNodes.toArray().length === 0) {
makeNewNode(_store, newNodeJSON, elem.node);
}
// else, generates however many nodes that are in the previous level
else {
prevLevelNodes.toArray().forEach(function(node, idx) {
newNodeJSON.parentId = node.get('id');
makeNewNode(_store, newNodeJSON, elem.node);
});
}
});
}
var makeNewNode = function(_store, newNodeJSON, node) {
console.log(newNodeJSON.parentId); // returns correct value
var newNode = _store.createRecord('node', newNodeJSON);
newNode.save();
var newNodeId = newNode.get('id');
if (newNode.get('parentId')) {
_store.find('node', newNode.get('parentId')).then(function(n) {
var cids = n.get('childIds');
console.log(newNodeId); // returns expected value
console.log(cids); // **DOESN'T RETURN AN EMPTY ARRAY**: returns array with [3,4]
cids.push(newNodeId);
console.log(n.get('childIds')); // returns array with [3,4]
n.save();
});
}
To top this off, this error happens 90% of the time, but 10% of the time it performs as expected. This seems to suggest that there's some sort of race condition, but I'm not sure where that would even be. Some places that I feel like might be causing issues: the ember-cli compilation, passing the entire _store in when making a new node, ember-data being weird, ember-localstorage-adapter being funky... no clue.
For anyone else who may have this problem in the future: the problem lies in two things.
In ArrayTransform, typically I am returning the value sans modification.
In my insert code, I'm passing the same JSON that I defined at the top of the function to makeNewNode.
This JSON contains a reference to a single childIds array; therefore, each new node that gets created uses this same reference for its childIds. Although this doesn't quite explain why the cids array wasn't empty before the push executed (perhaps this is some sort of compiler oddity or console printing lag), it explains why these both Level 3 children were in both Level 2 parents' childIds array.
tl;dr: pass by value vs pass by reference error

Categories

Resources