Published on

The art of problem solving πŸŽ‰

Authors
  • avatar
    Name
    Ayush Sinha
    Twitter
    @home

Introduction

Problem are something that most of us deal with everyday. It's said that how we approach a problem decides the outcome. So what if we can actually have an algorithm (steps) to approach a problem?

Note: many of these strategies are adapted from George Polya, whose book How To Solve ItΒ is a great resource for anyone who wants to become a better problem solver. Also credits to this course that I got aware about it.

Problem Solving Algorithm

There are 5 key steps to approach a problem

  • Understand the problem
  • Explore Examples
  • Break it down
  • Solve/Simplify
  • Look back and Refactor

1. Understanding the problem

  • Can I restate the problem in my own words?
  • What are the inputs for this problem?
  • What are the outputs that should come out of the problem?
  • Can the outputs be determined from the inputs? Do I have enough information to solve the problem?
  • How should I label the important pieces of data that're a part of the problem?

2. Explore Examples

  • Coming up with examples can help understanding the problem better. Some common examples can be -

    • Simple examples
    • More comples examples
    • Explore examples with empty inputs
    • Explore examples with invalid inputs
  • Provides sanity check for the solution

3. Break it down

  • Explicity write down the steps you need to take. This forces you to think about the code before you write it, which is a great step for catching misunderstandings or conceptual errors before you write a single line of code.

4. Simplify

When we're given with a problem, most of us jump right in, and if there's a part that we're not sure about and it bothers us, we spend out most of the time solving that. This can be considered a flaw in your problem solving process when your'e in an interview setting. A way to circumvent this situation can be -

  • Find the difficult part and keep it aside
  • Write a solution of all other parts of the problem
  • Try to incorporate the difficult part now, which in most of the cases you might reach if you have answer to all other part of the question.

5. Look back and Refactor

  • Can you check the result?
  • Can you derive the result differently?
  • Can you understand it at a glance?
  • Can you use the result or method for some other problem?
  • Can you improve the performance of your solution?
  • Can you think of other ways to refactor?
  • How have other people solved this problem?

Example

Here's an example with the entire process

Write a function which takes in a string and returns counts of each character in the string.

example.js

// Step 1 - Restate the problem
// so write a function that takes a string input and retuns the count of each character(s) that are there in that string
// what are the inputs ? a string value
// what is the expected output ? Not mentioned in the question, hence ask the question from the concerned person.

// Step 2 - Explore examples
// ask the use case for each of the input below, and what is allowed and what is not
"aaaaddsd" // a simple example {a:4, d:3, s:1} - allowed
"Hello" // an example with a mix of upper and lower case characters - allowed
"Hi! How are you?" // a more complex example with mix of special, upper and lowecase characters - allowed
"Hi! My contact is 2323223" // allowed
"" // an example with empty input - not allowed
undefined / null // an example with invalid input - not allowed


// Step 3 - Break down the problem
// also lets suppose, the expected output is an object that can have only alphabets, all lowercase

let charCount = (str) => {
  // check if string in a non null, non falsy value, if yes then return here
  // string should be string and not a number
  // declare an object to be returned
  // convert the entire string to lowercase
  // strip off all the characters other than the alphabets
  // loop over the remaining string of alphabets, check if the alphabet exisits in the object
  // if yes, increment the count with 1
  // else, introduce the character as the key in the object, with its value as 1 like {a : 1}
  // return the object
  if (!str || !isNaN(str)) return;
  let str = str.toLowerCase()
  let obj = {};
  // to keep only alphabets here, we can do this via a function that uses some kind of regex, but not superconfident about it, so left it for now and focus on other part of our solution
  let strArr = onlyAlpha(str);
  str = strArr.join("");
  for (let s in str) {
    if (obj.hasOwnProperty(str[s])) {
      obj[str[s]] += 1;
    } else {
      obj[str[s]] = 1;
    }
  }
  return obj;

}

// At this point we have most of the solution ready. For the part we need help, like developing a regex, we can always have a look at the documentation

// now lets work on the function to strip off all characters except for alphabets
// research a bit on regular expressions
let onlyAlpha = (arg) => {
  // if no argumnet, return
  // make an array to return
  // test each element with the regex.test method
  // if the character verifies to be an alphabet, push it in the array
  // return otherwise and jump to the next iteration
  // finally return the array
  if(!arg) return
  let arr = []
  const regex = new RegExp('[a-z]', 'i')
  for (let a in arr) {
    if (regex.test(arg[a])) {
      arr.push(arg[a]);
    }
  }
  return arr
}

// now we just have to use onlyAlpha in the above fn (and dont forget to declare this function above charCount fn, due to block scoping), to have the desired o/p