How to Solve It

Intro To Solving Problems

Programming, particularly web development, is made up of numerous parts that require various bits of knowledge and skill to do. While this course will focus on the concrete skills of web development using JavaScript, AngularJS, Node and MongoDB, There is one skill that transcends all the rest that we will be focusing on and coming back to throughout the course; Problem Solving.

In your future job after you finish V School you will, at some point, be required to learn new languages, new frameworks, new database systems, and new ways of organizing code for various reasons. Regardless of where your career takes you, you can always remain relevant and valuable if you learn some basic Computer Science principles and have the ability to solve problems.

Problem solving doesn't need to be a haphazard experience. We have a methodology for problem solving that will help you use specific steps and ask yourself particular questions in order to break down a problem and find a solution for each of it's parts.

Step 1: Understanding the Problem

The essence of beginning any problem is to break it down into sufficiently small pieces so that it can be understood.

Famous mathematician and teacher George Polya stated:

The worst may happen if the student embarks on computations or constructions without having understood the problem. It is generally useless to carry out the details without having seen the main connection or having made a sort of plan.

Here at V School we don't just want to teach you the steps of the various tasks to be done, we want to teach you concepts and problem solving skills.

Let's use a practical problem as an example.

Write a function that takes a string and capitalizes each word and returns it

The first part of understanding a problem is to dissect it into it's various parts:

  1. What are the data? What information are we given?
  2. What are the unknowns? What data don't we have that could help us solve this problem? Is there an unknown that we can solve for as an intermediate problem or can we look up how to do something as a step in our process?
  3. What is the condition to solve the problem?

Once we've dissected our problem into smaller parts and discovered what we know and don't know we need to use that information for the next step.

Step 2: Devising a Plan

We want to continue asking ourselves various questions about this problem. In the previous step we sought to understand the problem. Struggle to devise a plan may indicate either lack of knowledge or lack of understanding (or both) and it maybe worth it to revisit the first step and attempt to break the problem down further until you fully grasp what knowledge you must obtain to solve the problem.

  1. Is there a part of this plan that looks familiar?
  2. Can we use information from previous problems we've seen to solve this problem?
  3. Can the problem be restated in a different way?
  4. What tools or methods will be useful in solving this problem? Where could we search to find them.

Once we've identified several methods, tools, or features that can help us solve the problem we need to create a step-by-step plan. In writing code we often do this by writing "pseudo code" which is just writing the process in english rather than code

// capitalize the first letter of each word in a sentence

function titleCase (phrase) {
// separate each word in the phrase into an array
// iterate over each word 
// capitalize the first letter using .toLowerCase() method and string concatenation
// join the phrase back together and return it
}
Step 3: Executing the Plan

Once we've understand and devised a plan it's now time to execute it. It maybe tempting to write the code all at once, However, bugs and issues can arise if we're solving anything but an extraordinarily easy problem.

The key to executing the problem is checking each step as we write it so that we ensure we're getting the results we expect. We may even attempt putting in various inputs in order to test if our code or code segment still works in multiple use cases.

The easiest way to check each step in JavaScript is by using console.log to view the current state of our data.

function titleCase (phrase) {
    // separate each word in the phrase into an array
    var separatedWords = phrase.split(' ')
    console.log(separatedWords)  // ['I', 'am', 'separated']
// iterate over each word 
// capitalize the first letter using .toLowerCase() method and string concatenation
// join the phrase back together and return it
}

titleCase("I am separated")

Aside from step by step checking of our result, we also write Unit Tests on our code to make sure that it works. We'll get into Unit Tests in the future.

Step 4: Looking Back

After we've written and tested our code it's time to look back at our work. This phase can also be thought of as an optimization phase. After solving the problem it may become apparent that there is a better or simpler way to get the same thing done. We might find that our solution or parts of our solution are overly specific and can be generalized for reuse, or that our code can be refactored to be more readable.

Let's take for example a solution to our titleCase problem:

function titleCase (phrase) {
    var separatedWords = phrase.split(' ')
    for (var i = 0; i < separatedWords.length; i++) {
       var word = separatedWords[i];
       separatedWords[i] =   word.charAt(0).toUpperCase() + word.slice(1);
    }
var rejoined = separatedWords.join(' ')
return rejoined
}

After writing this code you may realize that while capitalizing the first letter of every word is useful that you may also want to capitalize only the first letter of word/sentence rather than everything. If we look closely we can see that there is one line that does that exactly and we can refactor this code to pull that out into it's own function.

function capitalCase (str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
}

function titleCase (phrase) {
    var separatedWords = phrase.split(' ')
    for (var i = 0; i < separatedWords.length; i++) {
       var word = separatedWords[i];
       separatedWords[i] = capitalCase(word)
    }
var rejoined = separatedWords.join(' ')
return rejoined
}

Now we can capitalize the first letter only as well as every word in a phrase/sentence. This also helps us clean up our code so that we can tell more precisely what we're doing. It's more readable.

Something else we may consider when looking back on our solution is potential edge cases, instances where we may use this function and it may not work correctly or produce an error. For example what if we have number literals in our phrase? Example: "I have 238 different ties in my closet." Will this phrase work? What happens when we attempt to capital case the number. If it does produce an error, how can we change our function to handle that use case?

Conclusion

Following these steps will help you solve complicated problems that feel like they're outside of your ability. If you can break down a problem into small, manageable pieces and solve everything you know how to do then it's easier to get help via Google, documentation, or someone else to do the things you're not sure how to do.