Recursively concatenate passed string

I’m writing a BST in JS and trying to concatenate a string passed into a member function without success. console.log() works but it automatically starts a new line for each entry.

"use strict";

class Node {
    constructor(dt) {
        this.data = dt;
        this.left = null;
        this.right = null;
    }
}

class BST {
    constructor() {
        this.root = null;
    }
    insert(parent, key) {
        if (!this.root) return this.root = new Node(key);
        else {
            if (key < parent.data) {
                if (!parent.left) {
                    parent.left = new Node(key);
                } else {
                    this.insert(parent.left, key);
                }
            } else if (key > parent.data) {
                if (!parent.right) {
                    parent.right = new Node(key);
                } else {
                    this.insert(parent.right, key);
                }
            }
        }
    }
    
    // Doesn't work
    printIN(parent, string) {
        if (parent) {
            this.printIN(parent.left);
            console.log(parent.data);
            string += " " + parent.data;
            // return string += " " + parent.data;
            this.printIN(parent.right);
        }
        return string;
    }
    
    // This works.
    // printIN(parent) {
    //     if (parent) {
    //         this.printIN(parent.left);
    //         console.log(parent.data);
    //         this.printIN(parent.right);
    //     }
    // }
}


let treeA = new BST();
let tree = null;

tree = treeA.insert(treeA.root, 5);
tree = treeA.insert(treeA.root, 7);
tree = treeA.insert(treeA.root, 3);
tree = treeA.insert(treeA.root, 14);


let string = [""];
string = treeA.printIN(treeA.root, string);
console.log();
console.log(string);

// treeA.printIN(treeA.root);

I want to print out the numbers on one single line, instead of them starting on a new line each time. I thought using string concatenation is the logical solution, but I can’t seem to make it work.

    // Doesn't work
    printIN(parent, string) {
        if (parent) {
            this.printIN(parent.left);
            console.log(parent.data);
            string += " " + parent.data;
            // return string += " " + parent.data;
            this.printIN(parent.right);
        }
        return string;

Answer

Another answer gave you a solution to your string problem. The snippet below has an alternative. But more importantly, it offers some significant clean-up to your OOP approach.

There are two main reasons this is necessary.

First, you have member functions of your object, which still require the object to be passed to them. That’s redundant and confusing. Any OOP function which doesn’t make reference to this should be a static function. But if you’re going to do OOP (I might argue against it in fact), then you shouldn’t use static functions as though they were methods. Thus insert and printIN should not have the parent parameter.

Second, you are dealing with a recursive structure. Nodes of a tree should have the same design as the tree itself. This makes all sorts of things easier. But you have a class Node and one called BST. And BST then has a root node, which should then be recursive. There is simply no reason for this that I can see. A BST by nature is a root node, with a data value (usually, although we can make that optional as you do) and potentially left and right nodes that are themselves BSTs.

Fixing the code to work like this simplifies it substantially. Here is one implementation:

class BST {
    constructor(data) {
        this.data = data;
    }
  
    insert(key) {
        if (!this.data) {
            this.data = key;
        } else {
            if (key < this.data) {
                if (!this.left) {
                    this.left = new BST(key);
                } else {
                    this.left.insert(key);
                }
            } else if (key > this.data) {
                if (!this.right) {
                    this.right = new BST(key);
                } else {
                    this.right.insert(key);
                }
            }
        }
    }

    printIN() {
        return (this.left ? this.left.printIN() + ' ' : '') +
               this.data +
               (this.right ? ' ' + this.right.printIN() : ''); 
    }
}


let treeA = new BST();

treeA.insert(5);
treeA.insert(7);
treeA.insert(3);
treeA.insert(14);

console.log(treeA.printIN());

There is no Node class here. Everything is done in BST. Moreover, BST objects don’t need a root node and the methods are simpler.

I would note, though that printIN is less useful than another function which we could use the same way:

    toArray() {
        return [
            ...(this.left ? this.left.toArray() : []), 
            this.data, 
            ...(this.right ? this.right.toArray() : [])
        ];
    }

which would allow

console.log(treeA.toArray()); //=> [3, 5, 7, 14]

and you could get your string just by joining that array with a space. This function strikes me as much more useful than the printIN one you have.

We could also add a static function:

    static fromArray(xs) {
        return xs .reduce (function (tree, x) {
            tree.insert(x)
            return tree
        }, new BST())
    }

that would allow us to write

tree = BST .fromArray ([8, 6, 7, 5, 3, 0, 9]);
tree .printIN() //=> [0, 3, 5, 6, 7, 8, 9]

I mentioned that I wouldn’t even do this with OOP. But I don’t do much that way these days, preferring functional approaches. So, if you’re curious, here’s another take on the problem altogether, using just functions, and trees that aren’t modified when you do an insert, but instead return new trees:

const bst = (xs) => 
  xs .reduce ((tree, x) => bstInsert (x, tree), {})

const bstInsert = (x, {left, data, right} = {}) => 
  data == undefined
    ? {data: x}
    : x < data
      ? {data, left: bstInsert (x, left), ...(right ? {right} : {})}
      : {data, ...(left ? {left} : {}), right: bstInsert (x, right)}

const inorder = ({left, data, right} = {}) => [
  ...(left ? inorder(left) : []), 
  data, 
  ...(right ? inorder(right) : [])
]

inorder (bst ([8, 6, 7, 5, 3, 0, 9])) //=> [0, 3, 5, 6, 7, 8, 9]

I’m not recommending that technique, specifically, but FP can be a powerful alternative to OOP.