Search code examples
javascriptnulldereference

Javascript - Remove references to my object from external arrays


I have a problem with dereferencing a Javascript object and setting it to NULL.

Here, I have a Folder implementation that supports recursive subdirectory removal. Please see my comments to understand my dilemma.

function Folder(name, DOM_rows) {
    this.name = name;
    this.files = [].concat(DOM_rows);
    this.subdirs = [];
}

Folder.prototype.AddDir(name, DOM_rows) {
   this.subdirs.push(new Folder(name, DOM_rows));
}

Folder.prototype.RemoveDir(folder) {
   var stack = [folder];
   while(stack.length > 0) {
      var cur = stack.pop();
      // do a post-order depth-first traversal, so dig to the deepest subdir:
      if(cur.subdirs.length > 0) {
          while(cur.subdirs.length > 0) { stack.push(cur.subdirs.pop()); }
      } else {
          // arrived at a leaf-level:
          cur.files = null;
          // now how do I delete cur from it's parent's subdirs array?
          // the only way I know how is to keep a "cur.parentDir" reference,
          // then find parent.subdirs[ index of cur ] and slice it out.
          // How can I do the JS-equivalent of *cur = NULL?
      }
   }
}

Solution

  • Note that you don't have as big a problem as you suspect, since all subdirectories but folder in your RemoveDir will be deleted from their parent's subdir by the stack.push(cur.subdirs.pop()); line

    To find a subdirectory in a parent, you could make use an object-as-dictionary rather than an array for subdirs:

    function Folder(name, DOM_rows, parent) {
        this.name = name;
        this.parent = parent;
        this.files = [].concat(DOM_rows);
        this.subdirs = {};
        this.subdirCount = 0;
    }
    
    Folder.prototype.AddDir = function (name, DOM_rows) {
        if (this.subdirs[name]) {
            return null;
        }
        ++this.subdirCount;
        return this.subdirs[name] = new Folder(name, DOM_rows, this);
    }
    

    Given a folder, you can remove the folder from the parent with:

    delete folder.parent.subdirs[folder.name];
    

    Here's a preorder version:

    Folder.prototype.RemoveDir = function (folder) {
      if (this.subdirs[folder.name] === folder) {
          var stack = [folder];
          while(stack.length > 0) {
              var cur = stack.pop();
              // pre-order
              delete cur.files;
              // if there's other processing to be done, now's the time to do it
              for (subdir in cur.subdirs) {
                  stack.push(cur.subdirs[subdir]);
                  delete cur.subdirs[subdir];
              }
              // it's unnecessary to set subdir count, since 'cur' has been deleted
              //cur.subdirCount = 0;
          }
          delete this.subdirs[folder.name];
          --this.subdirCount;
      }
    }
    

    And the recursive post-order version:

    Folder.prototype.RemoveChildren = function () {
        for (subdir in this.subdirs) {
            this.RemoveDir(this.subdirs[subdir]);
        }
    }
    
    Folder.prototype.RemoveDir = function (folder) {
        if (this.subdirs[folder.name] === folder) {
            folder.RemoveChildren();
            folder.files = [];
            delete this.subdirs[folder.name];
            --this.subdirCount;
        }
    }
    

    And the iterative post-order version:

    Array.prototype.top = function () { return this[this.length-1]; }
    
    Folder.prototype.RemoveDir = function (folder) {
      if (this.subdirs[folder.name] === folder) {
          var stack = [folder];
          while(stack.length > 0) {
              var cur = stack.top();
              if (cur.subdirCount > 0) {
                  for (subdir in cur.subdirs) {
                      stack.push(cur.subdirs[subdir]);
                      delete cur.subdirs[subdir];
                  }
                  cur.subdirCount = 0;
              } else {
                  stack.pop();
                  delete cur.files;
                  // other post-order processing
              }
          }
          delete this.subdirs[folder.name];
      }
    }
    

    Though, unless you need to take additional steps when processing deleted files & folders, a simple:

    Folder.prototype.RemoveDir = function (folder) {
      if (this.subdirs[folder.name] === folder) {
        delete this.subdirs[folder.name];
      }
    }
    

    should suffice.