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:
- Understand the Problem
- Explore Concrete Examples
- Break It Down
- Solve a Simpler Problem
- Use Tools Strategically
- Look Back and Refactor
The ecosystem around programming is filled with tools intended to make writing code easier. And indeed, developing a workflow that makes sense to you is essential to becoming a productive programmer and problem-solver.
Using the right tool for the job can help illuminate blocks when you’re solving a problem, and suggest ways to a solution. But if you use the wrong tool, or don’t know how to use the many tools available, your work can come grinding to a halt. This brings us to one of our most important strategies: using tools strategically.
Strategy #5: Use Tools Strategically
The idea of being comfortable with using tools to help solve problems is not unique to programming. For example, the Common Core Standards for Mathematics list not only content standards (i.e., what students need to know), but also higher-level Mathematical Practice standards. These standards are focused more on the process of thinking about and doing mathematics, rather than specific content. And the fifth one of these standards states that students should be able to “Use appropriate tools strategically.”
The same habits of mind emphasized by this mathematical practice standard are equally beneficial when trying to solve a problem with code. The text of the mathematical practice standard states, in part, that:
Mathematically proficient students consider the available tools when solving a mathematical problem…. Proficient students are sufficiently familiar with tools appropriate for their grade or course to make sound decisions about when each of these tools might be helpful, recognizing both the insight to be gained and their limitations…. Mathematically proficient students at various grade levels are able to identify relevant external mathematical resources, such as digital content located on a website, and use them to pose or solve problems. They are able to use technological tools to explore and deepen their understanding of concepts.
In a programming context, there are a number of tools you can use when you encounter a problem to help come to a resolution. Here are three of the most common:
- Use a linter. Linters can tremendously improve your productivity by catching bugs and syntax errors in your code before you run it. If you use Sublime Text, SublimeLinter is a great linting framework that supports a variety of programming languages. Atom also supports linters. If your workflow doesn’t incorporate some kind of linting, you’re really missing out.
- Get comfortable with error messages. Error messages can be intimidating at first. But if you know how to read them, they very often will point you to exactly what’s wrong with your code. If you’re looking to better understand errors in JavaScript, we’ve got you covered. Regardless of the language, it’s worth taking time to read about how errors work and some of the most common ones, so that you can tackle errors with confidence when they come up.
- Know how to pause code execution Being able to pause your code as it’s executing is another essential skill. Pausing execution allows you to inspect variables inside of functions, and investigate the state of your application before an error occurs. In JavaScript, the
debugger
keyword is a great way to debug your code; comfortability with the Chrome Dev Tools is even better. (Once again, we’ve got you covered.) Other languages have similar tools, and it’s one of the first things you should learn how to do when you’re learning a new language.
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.
This example requires some knowledge of JavaScript; if it doesn’t make sense to you, check out our JavaScript courses!
Suppose you try to be fancy, and solve this problem in JavaScript using reduce
, and regular expressions. Here’s what you come up with:
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; } },{}); }
When you try to run this code, however, you get the following error in the console:
charCount("Your PIN number is 1234!"); // VM269:5 Uncaught TypeError: Cannot read property 'o' of undefined // at <anonymous>:5:27 // at Array.reduce (native) // at charCount (<anonymous>:2:23) // at <anonymous>:1:1
As a novice JavaScript developer, this error message may not be at all helpful to you. But the stack trace tells us that that the error was originally triggered by line 5, column 27 of our program. That’s this line:
obj[char] = ++obj[char] || 1; ^
The message also tells us that JavaScript Cannot read property 'o' of undefined
. So something is going wrong presumably when we hit the second character in our string, the 'o'
in 'Your'
.
If you’re struggling to understand what’s happening, let’s throw a debugger in the code and step through it line by line:
function charCount(str) { return str.split("").reduce(function(obj, char) { char = char.toLowerCase(); if (/[A-Z0-9]/i.test(char)) { debugger obj[char] = ++obj[char] || 1; } },{}); }
The first time you hit the debugger, obj
has the value {}
; this makes sense, since we’re setting an empty object as the initial value for obj
inside of reduce.
But the second time we hit the debugger, at the next step in the iteration, obj
has a value of undefined
. Here’s where the problem lies! Inside of our callback to reduce
, we aren’t returning anything! Therefore, on our original line, when we tried to access obj[char]
, obj
had the value of undefined
, and trying to access a property on undefined
results in a TypeError
, as we’ve seen.
By understanding errors and knowing how to use simple debugging tools, we can quickly diagnose and fix the issue. In this case, we just need to remember to return obj
inside of the callback function:
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; },{}); }
This function works like a charm.
This completes our list of strategies to work through during the planning and execution phases of solving a problem. But just because you’ve solved a problem doesn’t mean that you’ve finished problem solving! In our next post, we’ll talk about one more strategy to use once you’ve completed the problem you originally set out to solve.