Recursion troubles with "return undefined" (JavaScript) - javascript

I'm stuck with recursion. I've read a few similar questions, but it brought me no relief.
My function works well inside, but it returns "undefined" at the end.
I have a binary tree. Now I'm trying to make a function, which can get me an answer: if any nodes on this level have children.
class tree_node {
constructor(n_array, parent) {
this.n_array = n_array;
this.has_children = false;
this.children = [];
if (parent != null) {
this.parent = parent;
this.level = this.parent.level + 1;
}
else {
this.level = 0;
}
// run the tree
this.child();
}
child() {
if (this.n_array.length != 1) {
this.has_children = true;
let m = Math.floor(this.n_array.length / 2);
let l = this.n_array.slice(0, m);
let r = this.n_array.slice(m);
const left = new tree_node(l, this);
const right = new tree_node(r, this);
this.children.push(left, right);
}
else return 0
}
get_if_node_has_children(node, level) {
console.log(node.level, node.has_children)
if (node.has_children && node.level < level) {
console.log("in loop")
node.children.forEach(element => {
return element.get_if_node_has_children(element, level);
});
}
else {
console.log("first else")
if (node.level == level && node.has_children) {
console.log("node.level == level && node.has_children " + node.n_array)
return true;
}
else {
console.log("return false")
return false;
}
}
}
show() {
console.log(this.n_array + " | Level: " + this.level + ". " + this.branch + " Has children = " + this.has_children);
if (this.has_children) {
this.children.forEach(element => {
return element.show();
});
}
else {
return 0;
}
}
}
get_if_node_has_children(node, level) work inside, so to speak. I've expected the exact behaviour and log. Except on thing: function returns "undefined". But I have no idea where I missed the point.
let root = [];
class tree_node {
constructor(n_array, parent) {
this.n_array = n_array;
this.has_children = false;
this.children = [];
// при создании экземпляра класса то parent == null
if (parent != null) {
this.parent = parent;
this.level = this.parent.level + 1;
} else {
this.level = 0;
}
// run the tree
this.child();
}
child() {
if (this.n_array.length != 1) {
this.has_children = true;
let m = Math.floor(this.n_array.length / 2);
let l = this.n_array.slice(0, m);
let r = this.n_array.slice(m);
const left = new tree_node(l, this);
const right = new tree_node(r, this);
this.children.push(left, right);
} else return 0
}
get_if_node_has_children(node, level) {
console.log(node.level, node.has_children)
if (node.has_children && node.level < level) {
console.log("in loop")
node.children.forEach(element => {
return element.get_if_node_has_children(element, level);
});
} else {
console.log("first else")
if (node.level == level && node.has_children) {
console.log("node.level == level && node.has_children " + node.n_array)
return true;
} else {
console.log("return false")
return false;
}
}
}
show() {
console.log(this.n_array + " | Level: " + this.level + ". " + "Has children = " + this.has_children);
if (this.has_children) {
this.children.forEach(element => {
return element.show();
});
} else {
return 0;
}
}
// CLASS END ===========================
}
root = new tree_node([1, 3, 5, 7, 9, ])
console.log("=== root.show() ===")
root.show();
console.log("=== let a = root.get_if_node_has_children(root, 2) ===")
let a = root.get_if_node_has_children(root, 2)
console.log(" a is " + a)

There are two problems:
return inside a callback (for instance, your forEach callback) just returns from the callback, not the function that called forEach. In general, in modern code, use for-of unless you need the index of the element.
You're not checking the result of the recursive call before returning it. But if the first child node you call it on returns false, you want to keep looking, rather than immediately returning false.
Fixing both:
get_if_node_has_children(node, level) {
console.log(node.level, node.has_children)
if (node.has_children && node.level < level) {
console.log("in loop")
for (const element of node.children) { // *** #1, using `for-of` instead of `forEach` so we can return below
const hasChild = element.get_if_node_has_children(element, level);
if (hasChild) { // *** #2, only return here if you got `true`
return true; // ***
} // ***
}
return false; // *** #2 part 2 -- didn't find it anywhere, return `false`
} else {
console.log("first else")
if (node.level == level && node.has_children) {
console.log("node.level == level && node.has_children " + node.n_array)
return true;
} else {
console.log("return false")
return false;
}
}
}
I'd reorganize that slightly though, which is easier if I remove some no-longer-needed logging:
get_if_node_has_children(node, level) {
console.log(node.level, node.has_children)
if (node.has_children) {
if (node.level === level) {
return true;
}
if (node.level < level) {
for (const element of node.children) {
if (element.get_if_node_has_children(element, level)) {
return true;
}
}
}
}
return false;
}

Related

My code is throwing an error, I need to find out the reason

I am getting expected error in my code and I have no idea why.
',' expected. The ide is pointing at the closing curly bracket of function remove, the curly bracket before the last curly bracket
The console says, uncaught syntax error: missing ) after argument list
Write a method remove that removes the specified item from the storage and returns the removed item and if not found
return a string Not Found, incase there are multiple items with the same name return the first one found.
class Safe {
constructor(safeSize) {
this.storage = [];
this.safeSize = safeSize;
}
insert(name, size) {
if (this.safeSize - size >= 0) {
this.safeSize -= size;
this.storage.push({
name: size
}); // push it as an object
return true;
}
return false;
}
remove(test) {
let shouldSkip = false;
this.storage.forEach(function(element, index) {
if (shouldSkip) {
return;
}
if (element === test) {
shouldSkip = true;
const removedItem = element;
this.storage.pop(element);
return ("The item" + removedItem + " Has been removed");
}
return (test + " Item not found");
}
}
}
> test cases:
>
> safe.insert("watermelon", 7); => true
> safe.insert("plate", 2); => true
>
> safe.remove("money"); => "Not Found"
> safe.remove("watermelon"); => {name: "watermelon", "size: 7"}
> safe.remove("watermelon"); => "Not Found"
There are some wrong codes in your code.
Please use this code.
class Safe {
constructor(safeSize) {
this.storage = [];
this.safeSize = safeSize;
}
insert(name, size) {
if (this.safeSize - size >= 0) {
this.safeSize -= size;
this.storage.push({
name, size
}); // push it as an object
return true;
}
return false;
}
remove(test) {
let shouldSkip = false;
let message = "";
this.storage = this.storage.filter( element => {
if(element.name == test && !shouldSkip) {
shouldSkip = true;
message = "The item " + element.name + " Has been removed"
return false;
}
return true;
});
if(!shouldSkip) message = test + " Item not found";
return message;
}
}
You're missing the closing parenthesis for your forEach function:
remove(test) {
let shouldSkip = false;
this.storage.forEach(function(element, index) {
if (shouldSkip) {
return;
}
if (element === test) {
shouldSkip = true;
const removedItem = element;
this.storage.pop(element);
return ("The item" + removedItem + " Has been removed");
}
return (test + " Item not found");
}) //<------
}

Returning Object Property in Javascript Undefined

I have this code:
function Tree() {
this.capacity = 1;
this.contents = 0;
this.children = [];
this.divided = false;
this.pour = function(amount) {
this.contents += amount;
if (this.contents <= 1) {
return;
}
if (!this.divided) {
this.divide();
}
amount = this.contents - 1;
this.contents = 1;
for (let child in this.children) {
this.children[child].pour(amount * .5);
}
}
this.divide = function() {
this.children = new Array(2).fill(0).map(x => new Tree());
this.divided = true;
return;
}
this.getContents = function(row, currentRow) {
if (currentRow === undefined) {
currentRow = 0;
}
if (row === currentRow) {
console.log('Contents:', this.contents)
return this.contents;
}
if (this.divided) {
console.log(row, currentRow)
currentRow++;
this.children[0].getContents(row, currentRow);
} else {
return;
}
}
}
Upon creating a tree, pouring into it, and getting its contents using this:
let tree = new Tree();
tree.pour(10);
tree.getContents(1);
It should return 1 because the second rows contents is 1. It logs 1 in the console but does not return the correct value. I am curious to what is going wrong.
Edit: I looked at switching it to a class and it did not solve the problem:
class Tree {
constructor() {
this.capacity = 1;
this.contents = 0;
this.children = [];
this.divided = false;
}
pour(amount) {
this.contents += amount;
if (this.contents <= 1) {
return;
}
if (!this.divided) {
this.divide();
}
amount = this.contents - 1;
this.contents = 1;
for (let child in this.children) {
this.children[child].pour(amount * .5);
}
}
divide() {
this.children = new Array(2).fill(0).map(x => new Tree());
this.divided = true;
return;
}
getContents(row, currentRow) {
if (currentRow === undefined) {
currentRow = 0;
}
if (row === currentRow) {
console.log('Contents:', this.contents)
return this.contents;
}
if (this.divided) {
console.log(row, currentRow)
currentRow++;
this.children[0].getContents(row, currentRow);
} else {
return;
}
}
}
The console.log you are seeing is the result of this call:
if (this.divided) {
console.log(row, currentRow)
currentRow++;
this.children[0].getContents(row, currentRow); //<-- here
// this is calling getContents() but ignores the return value
but in that case you don't actually return anything, so the inner console.log() fires but the return value is undefined.
I'm not really sure what the code is supposed to do, but returning a value when that condition is met results in a return value for the whole function:
if (this.divided) {
console.log(row, currentRow)
currentRow++;
return this.children[0].getContents(row, currentRow);
It logs 1 to the console because you call
tree.pour(10)
Current row is undefined because you do not pass it in the argument
if (currentRow === undefined) {
currentRow = 0;
}
if (row === currentRow) {
console.log('Contents:->', this.contents)
return this.contents;
}
So it mean currentRow is 0 and row(1) is not equals to currentRow (0) which is why it returns undefined.
} else {
return;
}

Printing trees in javascript

I need to print my tree but I don't know how to do it. I guess I may use recursion. I wonder if there are any easier ways? Here is the code.
var tree = new Object();
string = "23*4+5-";
tree = MakeMeATree(string, string.length - 1);
function MakeMeATree(string, t)
{
var tree = {};
if (t == 0) return;
tree.name = string.charAt(t);
t--;
if ( isOperand(string.charAt(t)))
{
tree.left = string.charAt(t);
tree.right = string.charAt(t-1);
t--;
tree.left = MakeMeATree(string, t);
}
if ( isOperand(string.charAt(t-1)))
{
tree.left = string.charAt(t-1);
tree.right = string.charAt(t);
t--;
tree.left = MakeMeATree(string, t);
}
else
{
tree.left = string.charAt(t);
tree.right = string.charAt(t-1);
t--;
}
return tree;
}
Also I am not sure about returnings, I mean should I use return tree both times? or just in the end?
UPD:
This function works I guess
PrintMeTree(tree);
function PrintMeTree(tree)
{
while (tree.name != undefined && tree.left != undefined && tree.right != undefined)
{
WScript.Echo(tree.name + " " + tree.left + " " + tree.right);
PrintMeTree(tree.left)
PrintMeTree(tree.right)
return;
}
}
This will make you a tree.
function isOperand(term) {
return /[0-9]/.test(term);
}
function MakeMeATree(string) {
var p = string.length - 1;
function consumeTree() {
var op = string.charAt(p--);
if (isOperand(op)) {
return op;
} else {
var right = consumeTree();
var left = consumeTree();
return {
name: op,
left: left,
right: right
};
}
}
return consumeTree();
}
var tree = MakeMeATree("23*5+5-");
console.log(JSON.stringify(tree));
/*
{
"name":"-",
"left":{
"name":"+",
"left":{
"name":"*",
"left":"2",
"right":"3"
},
"right":"5"
},
"right":"5"
}
*/

How does Javascript evaluate the right parenthesis?

I'm working on a calculator that takes an expression such as (5+4) and evaluates it by passing the buttons pressed to an array, and then building a parse tree from the data in the array.
What's interesting/strange, is that my code won't push the value of the right parenthesis to the array. Here is my code, could someone help me out?
The console.log activeButton shows that is the value of the button being pressed, but even when I placed calcArray.push() outside the if statements it would not push ) to an array.
$(document).ready(function(){
var calcArray = new Array();
$("input").click(function(){
var activeButton = this.value;
console.log(activeButton);
if(!isNaN(activeButton))
{
calcArray.push(parseInt(activeButton));
console.log(calcArray);
}
else if(activeButton === "=")
{
evaluate(buildTree(calcArray));
calcArray = [];
}
else
{
calcArray.push(activeButton);
}
});
});
The BuildTree code:
function BinaryTree(root) {
this.root = root;
this.activeNode = root;
}
function Node(element){
this.element = element;
this.parent;
this.rightChild;
this.leftChild;
this.setLeft = function(node){
this.leftChild = node;
node.parent = this;
};
this.setRight = function(node){
this.rightChild = node;
node.parent = this;
};
}
//methods
var buildTree = function(array)
{
var tree = new BinaryTree(new Node(null));
for(var i = 0; i < array.length; i++)
{
var newNode = new Node(array[i]);
if(array[i] == "(")
{
newNode.element = null;
tree.activeNode.setLeft(newNode);
tree.activeNode = newNode;
}
else if(array[i] == "+" || array[i] == "-" || array[i] == "/" || array[i] == "*")
{
tree.activeNode.element = newNode.element;
tree.activeNode.setRight(new Node(null));
tree.activeNode = tree.activeNode.rightChild;
}
else if(array[i] == ")")
{
if(tree.activeNode.parent == null)
{
;
}
else
{
tree.activeNode = tree.activeNode.parent;
tree.root = tree.activeNode;
}
}
else
{
tree.activeNode.element = newNode.element;
tree.activeNode = tree.activeNode.parent;
}
}
return tree.activeNode;
}
var evaluate = function(node){
var newNode1, newNode2;
newNode1 = new Node(null);
newNode1.parent = node;
newNode2 = new Node(null);
newNode2.parent = node;
if(node.leftChild == null && node.rightChild == null)
return node.element;
else{
newNode1.element = evaluate(node.leftChild);
newNode2.element = evaluate(node.rightChild);
if(newNode1.parent.element == "+")
{
return Number(newNode1.element) + Number(newNode2.element);
}
if(newNode1.parent.element == "-")
{
return newNode1.element - newNode2.element;
}
if(newNode1.parent.element == "*")
{
return newNode1.element * newNode2.element;
}
else
{
return newNode1.element / newNode2.element;
}
}
};
I just tried this out using your code and it worked fine passing the value as a string:
function pushButton (value) {
var activeButton = value;
console.log(activeButton);
if(!isNaN(activeButton))
{
calcArray.push(parseInt(activeButton));
console.log(calcArray);
}
else if(activeButton === "=")
{
evaluate(buildTree(calcArray));
calcArray = [];
}
else
{
calcArray.push(activeButton);
}
};
You aren't ever printing out the array in the last case (which is where the right paren would go), so are you sure it's not on the array and you just aren't seeing the visual feedback?
If so, we need to see more of your code. Try and setup a jsfiddle.

Doubts about NodeJS Module Development

I'm trying to code my very first NodeJS module, but I'm having trouble grasping some concepts.
Here's the code that I currently have (a genexer counter/generator):
"use strict";
var ret = require('ret');
module.exports = function (regex) {
if (Object.prototype.toString.call(regex) === '[object RegExp]') {
regex = regex.source;
}
else if (typeof regex !== 'string') {
regex = String(regex);
}
try {
var tokens = ret(regex);
}
catch (exception) {
return false;
}
return {
charset: '',
reference: [],
count: function (token) {
var result = 0;
if ((token.type === ret.types.ROOT) || (token.type === ret.types.GROUP)) {
if (token.hasOwnProperty('stack') === true) {
result = 1;
token.stack.forEach(function (node) {
result *= count(node);
});
}
else if (token.hasOwnProperty('options') === true) {
var options = [];
token.options.forEach(function (stack, i) {
options[i] = 1;
stack.forEach(function (node) {
options[i] *= count(node);
});
});
options.forEach(function (option) {
result += option;
});
}
if ((token.type === ret.types.GROUP) && (token.remember === true)) {
reference.push(token);
}
}
else if (token.type === ret.types.POSITION) {
}
else if (token.type === ret.types.SET) {
token.set.forEach(function (node) {
if (token.not === true) {
if ((node.type === ret.types.CHAR) && (node.value === 10)) {
}
}
result += count(node);
});
}
else if (token.type === ret.types.RANGE) {
result = (token.to - token.from + 1);
}
else if (token.type === ret.types.REPETITION) {
if (isFinite(token.max) !== true) {
return Infinity;
}
token.value = count(token.value);
for (var i = token.min; i <= token.max; ++i) {
result += Math.pow(token.value, i);
}
}
else if (token.type === ret.types.REFERENCE) {
if (reference.hasOwnProperty(token.value - 1) === true) {
result = 1;
}
}
else if (token.type === ret.types.CHAR) {
result = 1;
}
return result;
}(tokens),
generate: function () {
return false;
},
};
};
Questions:
am I calling count correctly on my first iteration? count: function (token) {}(tokens)?
how can I recursively call the count method? I get a "ReferenceError: count is not defined"
is this the correct (or best-practice) approach of defining a small module with several methods?
Forgive me for not posting 3 different questions, but I'm not very familiar with all the terminology yet.
The convention for immediately invoked closures is count: (function(args) {return function() {}})(args) but your way will also work in all environments.
You can't because count is a closure unfortunately - see 3.
If you want to use methods on your module inside your module I would declare the module outside of the return statement. If you want a good example of this see underscore/lodash source code.
So you can define your module using a declaration like the skeleton below
module.exports = function (regex) {
//...
var count = function(tokens) {
//...
return function() {
//...
var ret *= count(node);
return ret;
}
}
var mymod = {
count: count(tokens)
//...
};
//...
return mymod;
};

Categories

Resources