Breaking Down Code

Disclaimer: I am a student coder for a coding bootcamp program, not an educator. My blog posts may come off as confusing however the entire point is me figuring out the answers to my own questions and proving myself wrong as I write. The full answer will never be given or explained well initially however as I write I will do my best to get to a suitable solution. Happy reading!

One of the lessons from this week that really stumped me went a little something like this…

// Memorize an expensive function’s results by storing them. You may assume
// that the function only takes primitives as arguments.
// memoize could be renamed to oncePerUniqueArgumentList; memoize does the
// same thing as once, but based on many sets of unique arguments.
//
// _.memoize should return a function that, when called, will check if it has
// already computed the result for the given argument and return that value
// instead if possible.

My interpretation…

Create a function: Memoize
This function should only take primitive (aka simple) values as arguments. It should return a function that when called, will check if it has already computed the result and return it if it has.

Memoize is based on Once: A function that takes in another function and returns a new version of the function it takes in. It can only be called at most one time and any calls after that should return the original function. This means the code can only run once and should only have one solution.

We solved Once by first creating a new variable called alreadyCalled and setting it equal to false and another new variable called result. We then returned an empty function that determined if alreadyCalled was not false and instead was true, then apply the arguments to the given function and compute the result and set the answer equal to the result variable. After the function returns the result, set alreadyCalled to true and return the result of the function.

  _.once = function (func) {
    let alreadyCalled = false;
    let result;
    return function () {
      if (!alreadyCalled) {
        result = func.apply(this, arguments);
        alreadyCalled = true;
      }
      return result
    };
  };

In this code if there is already an answer then there is no need to run the return function, the result will automatically return as it is outside of those conditions. In any other case, if the result has not been computed, the computer will run the return function determining already called as true. Next the computer will apply the input arguments to the function using .apply and this which accesses the initial func parameter. Once the answer is produced the result will be set and alreadyCalled will be set officially to true. The result will be returned and the code will end.

How this applies to Memoize:
Memorize (a different function) is used to store the result of a function like the one above. Mem-o-ize is designed to check whether a result in Memorize has already been stored.

We start by making a memory variable within the Memoize function that will store the results of any function just like Memorize traditionally would.

 _.memoize = function(func) {

  const memory = {};

};

Say we have a function that takes 5 as an argument. The purpose of that function is to square the number 5 in this instance. The result would be 25. The way this would be stored in the memory object would appear like so:

{5: 25}

If 5 is used again as an argument in the future, then there will be a result of 25 again resulting in the memory object appearing like so:

{5: 25, 5: 25}

Memoize needs to check whether 5 has already been used as an argument and whether the results are the same as well. This would mean that there is a duplicate result which based on the tests we’re given would not be ideal or necessary.

The next step in Memoize is to determine what to do if the arguments we are given are not string values. In order to check for duplicates we need these value types to all be the same. If there is a string ‘5’ in the object but then a 5 in the object, it makes in more difficult for the computer to check for duplicate results in an efficient manner.

The part that stumped me wasn’t necessarily how the function should work, but more so how JSON.stringify worked. JSON.stringify is meant to compute a specified parameters position into a string to make it easier for the computer to read and recognize. From what I’ve read, in this instance JSON.stringify would stringify the position of the argument, not necessarily the argument itself. If an argument of 5, 6, 7, 8 are entered then 5 would hold call position 0, 6 would hold call position 1 and so on using typical indexing. I couldn’t understand why the position itself needed to be a string or how it made it easier for the computer to read. What I’ve learned is: Javascript naturally stores keys in objects as strings to make for a more stable key lookup. I had no idea. So to revamp everything I’ve said until now, what’s actually going on is JSON.stringify is positioning the arguments given to the input function as their own object. Under the hood, this would appear as

{"0": 5, "1": 6, "2": 7, "3": 8} 

What then happens is the function computes the action you are looking to do based on the given argument and it’s position. When the function computes the computer stores it as so:

Computer Model:

{{"0":5}: 25, {"1": 6}: 36, {"2": 7}: 49, {"3": 8}: 64}

Mental Model:

{5: 25, 6: 36, 7: 49, 8: 64} 

The true purpose of Memoize is speed. If the answer has already been created then it will be produced efficiently, otherwise Memoize will compute it itself and then return the result.

To complete this code we must create a variable that is set to utilizing JSON.stringify on the arguments that are input into the function.

 _.memoize = function(func) {

  const memory = {};

  return function() {

    const key = JSON.stringify(arguments); 

  };
};

Lastly, we create our condition. If there is a key and value already in the memory object, the value will be returned since the point of Memoize is to improve efficiency. If there is no key in the memory object that we created (aka if the memory is empty) a value for the stringified key variable that applies the argument on the function. Because the key has already been defined as the positioned arguments…

Ex: {“0”: 5, “1”: 6, “2”: 7, “3”: 8}

These will be our new keys and we can set the values as the result of completing the action on the argument via the function (Ex: squaring each argument) and we return the values in the memory.

 _.memoize = function(func) {

  const memory = {};

  return function() {

    const key = JSON.stringify(arguments); 

    if (!(key in memory)) {

      memory[key] = func.apply(this, arguments);

    }

    return memory[key];

  };
};

This lesson taught me more about how to use JSON.stringify, how it works and why it is necessary to update functions we have created in the past to improve efficiency for our code. Coding is all about efficiency and finding new pathways to limit as many bugs as possible. Thank you for reading and until next time!

Leave a Reply