Problem Solving Strategies: Look Back and Refactor

Welcome to Rithm’s series on problem-solving strategies. If you’re just joining us, you may want to start at the beginning. Here’s a list of the articles we’ve written:

  1. Understand the Problem
  2. Explore Concrete Examples
  3. Break It Down
  4. Solve a Simpler Problem
  5. Use Tools Strategically
  6. Look Back and Refactor

It can be tempting to think that your work is done after you’ve solved a problem, especially if the problem has been a difficult one. But as Polya writes in How To Solve It, reflection is an important part of effective problem solving:

Even fairly good students, when they have obtained the solution of the problem and written down neatly the argument, shut their books and look for something else. Doing so, they miss an important and instructive phase of the work. By looking back at the completed solution, by reconsidering and reexamining the result and the path that led to it, they could consolidate their knowledge and develop their ability to solve problems…There always remains something to do; with sufficient study and penetration, we could improve any solution, and, in any case, we can always improve our understanding of the solution.

In this final post, we’ll take a look at this final strategy from the standpoint of programming.

Strategy #6: Look Back and Refactor

After you’ve solved a problem, it’s valuable to look back at what you’ve done and reflect on your solution. Polya offers up a few questions worth asking yourself:

Can you check the result?

This most important question to ask: did you actually solve the problem? Does your solution work in all cases, or are there any edge cases you overlooked? If you try your solution out with different inputs, do you get the results you expect?

Can you derive the result differently?

There’s very rarely only one way to solve a problem. If you were working on different approaches, can you see more than one of them all the way through to a final solution? If you were focused on one approach throughout, can you take a step back and think of any other ways to solve the problem?

Can you see it at a glance?

In other words, how straightforward and intuitive is your solution? Can you explain it in simple terms to another person? If not, can you think of ways to simplify your approach?

Can you use the result, or the method, for some other problem?

One of the great benefits to solving a problem is that it helps develop your intuition for other problems. Whenever you write down a solution to a problem, it’s worth asking yourself what similarities you see between this problem and others you may have encountered in the past. What are the big ideas in your solution, the ones you can explain without writing any code at all? It’s these ideas that you’ll typically be able to transfer to other problems.

Here are a few other questions you can ask yourself, related to the ones above but focused specifically on writing code.

Can you improve the performance of your solution?

How does the performance of your solution improve as the size of the input grows? Can you think of a way to improve that performance? And even more importantly, does improving the performance matter, or is the performance fine for all use cases of what you’re building?

Can you think of any other ways to refactor your code?

Is your code readable by others? Does it follow conventions of the language? Is the indentation consistent? Remember that when you’re on the job, you’ll be writing code that other people will need to read, so it’s important for your code to follow conventions of the industry and be understandable by people who aren’t you.

How have other people solved this problem?

If you have trouble thinking of other ways to solve a problem, ask other people for their opinion. Soliciting feedback from others is a great way to learn about other approaches. This is also what makes Codewars such a valuable resource: whenever you solve a problem, you can view every other solution that anyone has submitted!

An Example

Let’s return to our coding example from before:

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

Imagine that you come up with the following solution:

function charCount(str) {
var obj = {};
for (var i = 0; i < str.length; i++) {
var char = str[i].toLowerCase();
if (/[a-z0-9]/.test(char)) {
if (obj[char] > 0) {
obj[char]++;
} else {
obj[char] = 1;
};
}
}
return obj;
}

Let’s run through some of the reflection questions listed above.

Can you check the result?

When it comes to programming, this typically means checking your result on a wide variety of inputs. Or, even better, write some tests and make sure your function passes them!

Can you derive the result differently?

What other tools could you have used to solve the problem? Can you solve the problem without using a for loop? Can you solve it using a more sophisticated regex? What other techniques might be applicable? Here are a couple of alternative solutions (one of which we saw in the previous strategy):

// option 1 - using reduce!
function charCount(str) {
return str.split("").reduce(function(obj, char) {
char = char.toLowerCase();
if (/[A-Z0-9]/i.test(char)) {
obj[char] = ++obj[char] || 1;
}
return obj;
},{});
}
// option 2 - more regex, and some ES2015!
function charCount(str) {
return str.toLowerCase()
.split('')
.sort()
.join('')
.match(/([a-z0-9])\1*/g)
.reduce(function(prev, cur) {
return Object.assign(prev, {[cur[0]]: cur.length})
}, {});
}

Can you see it at a glance?

Put another way, what are the big ideas in your solution? For this problem, you might say something like “loop through the string and create an object that keeps a running tally of how many times each letter or number appears.”

Can you use the result, or the method, for some other problem?

Once you’re able to count the frequency of characters in a string, there are a number of other statistical questions you can answer. Here are a few:

  • What’s the average frequency for characters in the string?
  • Which character appears the most often? Which character appears the least often?
  • What are the top two most frequent characters? Top 5? Top n, for any n?
  • How many unique alphanumeric characters are in the string?

What else are you curious about?

Can you improve the performance of your solution?

From a Big-O standpoint, not all of these solutions are created equal. If you know about time or space complexity, try analyzing these algorithms to see how they compare!


This list of questions isn’t intended to be exhaustive: if you think of some others, you should absolutely add them to your own process of reflection!

Similarly, this list of problem solving strategies isn’t meant to be exhaustive, either. But if you get comfortable with the strategies presented in this series, it should help you develop a much more systematic approach to problem-solving.

Good luck, and as always, let us know if you have any questions!

Written by Matt