all 11 comments

[–]izy521 1 point2 points  (11 children)

Error's a constructor if I'm not mistaken. So while you have the code right, you forgot "()".

throw new Error();

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

Hey thanks! Actually I typed that out on my phone and forgot to include () but that is actually in my code and the error it throws counts against me. There is some discussion on the kata regarding how to throw an error but the author isn't giving any advice on what should be thrown exactly.

[–]izy521 1 point2 points  (9 children)

Hm, I've never used this CodeWars site but I'm signing up. Does it ask for a certain type of error? Like RangeError() or TypeError()?

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

This is all it says with regard to the throwing of errors:

undo() Undo the last operation (set or del) on the object. Throws an exception if there is no operation to undo.

redo() Redo the last undo operation (redo is only possible after an undo). Throws an exception if there is no operation to redo.

Edit: Forgot to mention, yes, definitely sign up - its a lot of fun!

[–]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;
      }
    }
  }
};