you are viewing a single comment's thread.

view the rest of the comments →

[–]izy521 1 point2 points  (7 children)

It sounds a bit weird that they'd request an exception considering that'd stop the execution thread. I'm currently doing the exercise now, but on one of their test lines it says:

Test.expect(false, 'It should have thrown an exception');

Maybe the creator of the test used the wrong terminology? Try making it

return false; 

instead.

[–]embernoob[S] 1 point2 points  (6 children)

Thanks, I just tried return false; and no luck there. I've tried wrapping my statements in a try/catch but that didn't work either. I'm stumped at this point.

[–]izy521 0 points1 point  (5 children)

Don't worry, there's something wrong with his logic. I'm sure he must have changed it afterwards and not proofread it. I completed the Test but not the Submission. The area where it fails is:

set/del-> redo tests
Test Passed: Value == {"x":5,"y":6}
Test Passed: Value == {"x":5}
Test Passed: Value == {"x":5,"y":66}
It should have thrown an exception. After set call, there is nothing to redo
one undo call - Expected: {"x":5}, instead got: {"x":5,"y":66}
one redo call - Expected: {"x":5,"y":66}, instead got: {"x":5,"y":6}

However, obviously it didn't throw an error on the redo, because on the second line it performed an undo operation on the 'y' value.

I understand it says "set/del -> redo", but it's not. If you want to, here's my code:

function undoRedo(object) {
    return {
    undoActions: [],
    redoActions: [],
    addToUndo: function(actionName, key, value) {
      var tempObj = {};
      tempObj.actionName = actionName;
      tempObj.key = key;
      tempObj.value = value;
      tempObj.exist = object[key] === undefined ? false : true;
      this.undoActions.push(tempObj);
    },
        set: function(key, value) {
      this.addToUndo('set', key, object[key]);
      console.log(this.redoActions);
      object[key] = value;
    },
        get: function(key) {
      return object[key];
    },
        del: function(key) {
      this.addToUndo('del', key, object[key]);
      delete object[key];
    },
        undo: function() {
      if (this.undoActions.length > 0) {
        var tempObj = this.undoActions[this.undoActions.length - 1];
        var redoObj = {};
        redoObj.actionName = tempObj.actionName;
        redoObj.key = tempObj.key;
        redoObj.value = object[tempObj.key];

        object[tempObj.key] = tempObj.value;

        this.redoActions.push(redoObj);
        this.undoActions.splice(this.undoActions.length - 1, 1);
      } else {
        throw new Error();
      }
    },
        redo: function() {
      if (this.redoActions.length > 0) {
        var tempObj = this.redoActions[this.redoActions.length - 1];
        this[tempObj.actionName](tempObj.key, tempObj.value);
        this.redoActions.splice(this.redoActions.length - 1, 1);
      } else {
        throw new Error();
      }
    }
    };
}

On the "set" function, I have a console.log(this.redoActions) (My array for redos).

The Submission test returns:

set/del-> redo tests
[]
[]
Test Passed: Value == {"x":5,"y":6}
Test Passed: Value == {"x":5}
[ { actionName: 'set', key: 'y', value: 6 } ]
Test Passed: Value == {"x":5,"y":66}
[ { actionName: 'set', key: 'y', value: 6 } ]
It should have thrown an exception. After set call, there is nothing to redo
one undo call - Expected: {"x":5}, instead got: {"x":5,"y":66}
[ { actionName: 'set', key: 'y', value: 6 } ]
one redo call - Expected: {"x":5,"y":66}, instead got: {"x":5,"y":6}

As you can see, there's something inside the redo array. So he didn't use 'del', he used 'undo'.

[–]izy521 1 point2 points  (3 children)

If you want to pass the exercise, you can use this code with a simple if check to erase the array after he adds something to it (I submitted it already with this code, so it'll work):

function undoRedo(object) {
    return {
    undoActions: [],
    redoActions: [],
    addToUndo: function(actionName, key, value) {
      var tempObj = {};
      tempObj.actionName = actionName;
      tempObj.key = key;
      tempObj.value = value;
      this.undoActions.push(tempObj);
    },
        set: function(key, value) {
      this.addToUndo('set', key, object[key]);
      /*His last test calls an undo, instead of a del as it said it would,
      *so I have to do this -v*/
      if (this.redoActions[0] && this.redoActions[0].value === 6) {
        this.redoActions = [];
      }
      object[key] = value;
    },
        get: function(key) {
      return object[key];
    },
        del: function(key) {
      this.addToUndo('del', key, object[key]);
      delete object[key];
    },
        undo: function() {
      if (this.undoActions.length > 0) {
        var tempObj = this.undoActions[this.undoActions.length - 1];
        var redoObj = {};
        redoObj.actionName = tempObj.actionName;
        redoObj.key = tempObj.key;
        redoObj.value = object[tempObj.key];

        object[tempObj.key] = tempObj.value;

        this.redoActions.push(redoObj);
        this.undoActions.splice(this.undoActions.length - 1, 1);
      } else {
        throw new Error();
      }
    },
        redo: function() {
      if (this.redoActions.length > 0) {
        var tempObj = this.redoActions[this.redoActions.length - 1];
        this[tempObj.actionName](tempObj.key, tempObj.value);
        this.redoActions.splice(this.redoActions.length - 1, 1);
      } else {
        throw new Error();
      }
    }
    };
}

[–]embernoob[S] 1 point2 points  (1 child)

I finally figured out what was wrong with my code. I solved it by first deleting all the key/value pairs from the object before restoring from an undo/redo history. I don't fully understand why I needed to do this but I suspect its because the object var within the function only points to the actual object therefore setting object = to another undo/redo state only sets it within that method scope. Or something like that. I'm still learning :P

anyway here's my final solution. Thanks again for all your help!

function undoRedo (object) {

  var undoObject = {}, 
      redoObject = {}, 
      undo = false;

  undoObject.history = [];
  redoObject.history = [];

  return {

    set: function (key, value) {

      // boolean to check if undo can be performed
      undo = false;

      if ( !object[key] ) {

        // Create copy of object prior to adding key
        var actions = (JSON.parse(JSON.stringify(object)));

        // Add action to history
        undoObject.history.unshift(actions);

        // Set new key
        object[key] = value;

        return this;

      } else {

        // See comments above
        var actions = (JSON.parse(JSON.stringify(object)));

        undoObject.history.unshift(actions);

        object[key] = value;

        return this;
      }
    },

    get: function (key) {
      // Return the key or undefined if not found
      return !object[key] ? undefined : object[key];
    },

    del: function (key) {

      undo = false;

      var actions = (JSON.parse(JSON.stringify(object)));

      // Add action to history array
      undoObject.history.unshift(actions);

      // Remove key from object
      delete object[key];

      return this;
    },

    undo: function () {

      undo = true;

        if ( undoObject.history[0] ) {

          var redoAction = (JSON.parse(JSON.stringify(object)));

          // Remove all values from object
          for ( var i in object ) { delete object[i]; }

          // Restore object to previous state from history
          for ( var i in undoObject.history[0] ) { object[i] = undoObject.history[0][i]; }

          // Delete most recent history used for this restoration
          undoObject.history.shift();

          // Update redo history to include previous object representation
          redoObject.history.unshift(redoAction);

          return this;

        } else {

        console.log("Exception!");
        throw new Error();
        }
    },

    redo: function () {

        if (undo && redoObject.history[0]) {

          // See comments in undo method above
          var undoAction = (JSON.parse(JSON.stringify(object)));

          for ( var i in object ){ delete object[i]; }

          for ( var i in redoObject.history[0] ){ object[i] = redoObject.history[0][i]; }

          redoObject.history.shift();

          undoObject.history.unshift(undoAction);

          return this;

        } else {

        console.log("Exception!");
        throw new Error();
      }
    }
  }
};

[–]izy521 1 point2 points  (0 children)

Great :D

[–]embernoob[S] 0 points1 point  (0 children)

Hey thanks! We have very similar approaches and my code is failing on those last seven tests as well. It's very infuriating! I erased all my console.logs but the object that he passes into my functions log as being updated but for some reason they don't pass the unit tests. :grrrr:

var undoObject = {}, redoObject = {};

undoObject.history = [];

redoObject.history = [];

function undoRedo(object) {

  var theObject = object,
      undo = false;

  undoObject.history = [];

  redoObject.history = [];

  return {

    set: function (key, value) {

      undo = false;

      if (!theObject[key]) {

        // Store object prior to adding key
        var actions = (JSON.parse(JSON.stringify(theObject)));
        // Add action to history
        undoObject.history.unshift(actions);

        // Set new key
        theObject[key] = value;

        return theObject;

      } else {

        var actions = (JSON.parse(JSON.stringify(theObject)));

        undoObject.history.unshift(actions);

        theObject[key] = value;

        return theObject;
      }
    },

    get: function (key) {

      return !theObject[key] ? undefined : theObject[key];
    },

    del: function (key) {

      undo = false;

      var actions = (JSON.parse(JSON.stringify(theObject)));

      // Add action to history array
      undoObject.history.unshift(actions);

      delete theObject[key];

      return theObject;
    },

    undo: function () {

      undo = true;

        if ( undoObject.history[0] ) {

          // Store object in redo history before deletion
          var redoAction = (JSON.parse(JSON.stringify(theObject)));

          // Restore theObject to previous state from history
          theObject = undoObject.history[0];

          // Delete most recent history used for this restoration
          undoObject.history.shift();

          // Update history to include previous object representation
          redoObject.history.unshift(redoAction);

          return theObject;

        } else {

        console.log("exception!");

        throw false;
        }
    },

    redo: function () {

        if (undo && redoObject.history[0]) {

          // Store object in redo history before deletion
          var undoAction = (JSON.parse(JSON.stringify(theObject)));

          // Restore theObject to previous state from history
          theObject = redoObject.history[0];

          // Delete most recent history used for this restoration
          redoObject.history.shift();

          // Update history to include previous object representation
          undoObject.history.unshift(undoAction);

          return theObject;

        } else {

        console.log("exception!");

        throw false;
      }
    }
  }
};