Be creative while inventing ideas, but be disciplined while implementing them.
In this module you will explore different ways to break down and solve coding challenges. Along with structured workflows for approaching challenges, you will learn the JavaScript you need to write clear and maintainable solutions including: reading and writing tests, higher order functions, arrays and object.
What does the function do? What are itβs arguments and itβs return value? How could you use it in a program? Behavior is all about what your function looks like "from the outside", without caring about what is written inside.
Functions behavior is generally described using documentation, unit tests and use cases:
Documentation (JSDoc comment)
/**
* repeats a string a specific number of times
* @param {string} [text=''] - the string to repeat. defaults to empty string
* @param {number} [repetitions=1] - how many times to repeat. defaults to 1
* repetitions cannot be negative, and must be an integer
* @return {string} the text repeated as many times as repetitions
*/
Unit Tests (pass/fail assertions)
describe('repeats a string any number of times:', () => {
describe('an empty string', () => {
it('should repeat "" 0 times', () => {
expect(repeatString('', 0)).toEqual('');
});
it('should repeat "" 10 times', () => {
expect(repeatString('', 10)).toEqual('');
});
it('should repeat "" 100 times', () => {
expect(repeatString('', 100)).toEqual('');
});
});
describe('zero repetitions', () => {
it('a non-empty string repeated 0 times -> ""', () => {
expect(repeatString('asdf', 0)).toEqual('');
});
it('a longer string repeated 0 times -> ""', () => {
expect(repeatString('tommywalk', 0)).toEqual('');
});
});
describe('standard use cases', () => {
it('should repeat a phrase 3 times', () => {
expect(repeatString('go to school', 3)).toEqual(
'go to schoolgo to schoolgo to school'
);
});
it('should repeat phrases with punctuation', () => {
expect(repeatString('"Go!", said Dr. Seuss?', 2)).toEqual(
'"Go!", said Dr. Seuss?"Go!", said Dr. Seuss?'
);
});
it('should repeat strings with special characters', () => {
expect(repeatString('\\ \n \t s', 2)).toEqual('\\ \n \t s\\ \n \t s');
});
});
describe('default values', () => {
it('should repeat 1 time by default (second parameter)', () => {
expect(repeatString('asdf')).toEqual('asdf');
});
it('should repeat "" by default (first parameter)', () => {
expect(repeatString()).toEqual('');
});
});
});
Use Cases ("real-world" examples)
// repeating a string inside an I/O loop
let repeatedText = '';
while (true) {
const userString = promptForSomething('input a string to repeat');
const userRepetitions = promptForNumber('how many times to repeat it?');
const repeatedInput = repeatString(userString, userRepetitions);
const userConfirmed = confirm(`is this correct: "${repeatedInput}"`);
if (userConfirmed) {
repeatedText = repeatedInput;
break;
}
}
console.log(repeatedText);
// repeating a string from the DOM
const userString = document.getElementById('text-input').value;
const userRepetitions = document.getElementById('number-input').value;
const repeatedInput = repeatString(userString, userRepetitions);
document.getElementById('repeated-value-display').innerHTML = repeatedInput;
How do you approach solving the problem? There are many strategies to solve the same problem! A way to practice strategy is to think of transforming the arguments to the return value in small steps, focusing on the data not the code. This is the realm of flow charts, diagrams, and pseudo-code.
One way to approach strategy is to solve the problem a few different ways by hand, writing what you expect to change in memory at each step. Like if you were the debugger and you couldn't see the source code. Using a pencil and paper is a the best way to go, pick a few test cases and see how you'd solve them manually.
Here are four possible strategies to approach repeating a string. Each one is written as block comment with step-by-step goals focusing on what should happen at each step, not how it will happen. This type of comment is helpful to include in your code:
Iterate until string is long enough
/* iterating until the new string's length is correct
repeatString(text, repetitions) =>
1. calculate the final length for the new string
2. create a new string to fill with many text's
3. iterate as long as the new string is too short
a. check if the new string is long enough
stop if it is, keep going if it is not
b. append text to the new
c. repeat
return: the new repeated string
*/
Iteration with a stepper variable
/* iterating over the number of repetitions
repeatString(text, repetitions) =>
1. create a new string to fill with many text's
2. create a stepper variable, starting at 0
3. iterate from 0 to repetitions
a. check if stepper is still less than repetitions
keep going if it is, otherwise stop iterating
b. append text to the new string
c. increment the stepper
d. repeat
return: the new repeated string
*/
Recurse with base-case 0
/* recursion with base-case 0
i'm using 0 as the base-case because that is the fewest possible repetitions
zero repetitions is an empty string, so if repetitions is 0 it will return ''
otherwise i'll need to combine the text with a string that has one fewer reptitions
repeatString(text, repetitions) =>
base-case: repetitions is 0
return: an empty string
recursive case: repetitions is greater than 0
nextRepetitions = subtract one from repetitions
recursedValue = recursively call repeatString with text and nextRepetitions
return: text + recursedValue
*/
Native JS methods
/* use built-in .js methods
repeatString(text, repetitions) =>
1. make sure the data is the correct type and format for the method you're using
2. use the method
return: the result
*/
Which language features and which lines of code can you use to make your strategy a reality? There are many ways to code the same strategy. let's look at multiple implementations for each strategy described above, all of these functions will pass the unit tests written in the Behavior section:
While loop, true and break
/* unconventional and pretty old-school
there is a lot of reinventing the wheel
while loops are designed to check conditions
this is not the simplest solution to read or maintin
*/
const repeatString = (text = '', repetitions = 1) => {
const finalLength = text.length * repetitions;
let repeatedText = '';
while (true) {
if (repeatedText.length === finalLength) {
break;
}
repeatedText = repeatedText + text;
}
return repeatedText;
};
While loop, logic in head
/* the cleanest implementation for this strategy
it uses the language feature designed for this type of strategy
*/
const repeatString = (text = '', repetitions = 1) => {
const finalLength = text.length * repetitions;
let repeatedText = '';
while (repeatedText.length < finalLength) {
repeatedText += text;
}
return repeatedText;
};
For loop with only a condition check
/* not the best implementation, it's confusing to read
this strategy does not use stepping, and for loops are designed for stepping
implementing this strategy with a for loop is putting a square peg in a round hole
when someone sees a for loop they expect it to be used like a for loop
this implementation uses a for loop like a while loop
the computer doesn't care, but the intention is confusing for other devs
*/
const repeatString = (text = '', repetitions = 1) => {
const finalLength = text.length * repetitions;
let repeatedText = '';
for (; repeatedText.length < finalLength; ) {
repeatedText += text;
}
return repeatedText;
};
While loop, true and break
/* unconventional and pretty old-school
there is a lot of reinventing the wheel
while loops are designed to check conditions
this is not the simplest solution to read or maintain
*/
const repeatString = (text = '', repetitions = 1) => {
let repeatedText = '';
let count = 0;
while (true) {
if (count === repetitions) {
break;
}
repeatedText += text;
count++;
}
return repeatedText;
};
While loop, condition in head
/* a better way to user the while loop since the condition is known
easier to read and more conventional than the previous implementation
maybe you find this easier to read than a for loop
*/
const repeatString = (text = '', repetitions = 1) => {
let repeatedText = '';
let count = 0;
while (count < repetitions) {
repeatedText = repeatedText + text;
count++;
}
return repeatedText;
};
For loop
/* the cleanest implementation for this strategy
it uses the language feature designed for stepping
*/
const repeatString = (text = '', repetitions = 1) => {
let repeatedText = '';
for (let count = 0; count < repetitions; count++) {
repeatedText += text;
}
return repeatedText;
};
Ternary Operator
// in all it's ES6 one-line glory
// some people find this easier to read than conditionals
const repeatString = (text = '', repetitions = 1) =>
repetitions === 0 ? '' : text + repeatString(text, nextRepetitions - 1);
Conditional Statement
// good old fashioned conditional blocks
// some people find this easier to read than ternaries
const repeatString = (text = '', repetitions = 1) => {
if (repetitions === 0) {
return '';
} else {
const nextRepetitions = repetitions - 1;
const oneRepetitionShort = repeatString(text, nextRepetitions);
return text + oneRepetitionShort;
}
};
Conditional Statement (tail-call recursion)
/* this implementation is "backwards compatible"
that means that it has a different signature (the extra `repeated` parameter)
but can still replace previous implementations without breaking anything
*/
const repeatString = (text = '', repetitions = 1, repeated = '') => {
if (repetitions === 0) {
return repeated;
} else {
const nextRepeated = repeated + text;
const nextRepetitions = repetitions - 1;
return repeatString(text, nextRepetitions, nextRepeated);
}
};
Sting.prototype.repeat
// short and sweet, no room for mistakes
const repeatString = (text = '', repetitions = 1) => text.repeat(repetitions);
Array.prototype.fill
// less clear and more complex, but still pretty clear to read
const repeatString = (text = '', repetitions = 1) => {
const oneEntryPerRepetition = Array(repetitions).fill(text);
const repeatedString = oneEntryPerRepetition.join('');
return repeatedString;
};
- Getting Started
- Study Tips
- About Programming
- Learning Objectives
- Suggested Study
- Break-Down
- Class Recordings
- study.hackyourfuture.be
- home.hackyourfuture.be
You will need NPM and nvm on your computer to study this material
- Clone this repository:
git clone [email protected]:HackYourFutureBelgium/behavior-strategy-implementation.git
(SSH)
cd
into the repositorycd behavior-strategy-implementation
- Install dependencies:
npm install
Using a browser with good DevTools will make your life easier: Chromium, FireFox, Edge, Chrome
- Install or update the
study-lenses
package globallynpm install -g study-lenses
(if you do not have it already)npm update -g study-lenses
(if you already have it installed)
- Run the
study
command from your CLIstudy
- The material will open in your default browser, you're good to go!
- if a
.test.js
file does not work, you can manually check on tests from the config options
- if a
If you have a windows computer and get this error:
... /study.ps1 cannot be loaded because running scripts ...
follow the instructions in this StackOverflow answer, that should take care of it ; )
The debugger built into VSCode is very good, it's all you'll need. Don't forget to set breakpoints!
- Running files in
.js
(without.test
):- debugger: open the file, select the debug option from your VSCode side-bar, choose the
Current JS File (no tests)
option, and click the triangle button. (hint you will need to use breakpoints) - console:
node path/to/file.js
, simple and easy
- debugger: open the file, select the debug option from your VSCode side-bar, choose the
- Running files ending in
.test.js
or.spec.js
:- debugger: open the file, select the debug option from your VSCode side-bar, choose the
Current JS File (with tests)
option, and click the triangle button. (hint: you will need to use breakpoints) - console: files ending in .test.js or .spec.js must be run with
npm run test -- path/to/file.test.js
.
- debugger: open the file, select the debug option from your VSCode side-bar, choose the
- Don't rush, understand! Programming is hard.
- The examples and exercises will still be there to study later.
- It's better to fail tests slowly and learn from your mistakes than to pass tests quickly and not understand why.
- Don't skip the examples! Understanding and experimenting with working code is a very effective way to learn programming.
- Write lots of good comments, learn more about this in ./studying-javascript
- Practice Pair Programming: two people, one computer.
- Take a look through the Learning From Code guide for more study tips
If you can't finish all the material in this repository, that's expected! Anything you don't finish now will always be waiting for you to review when you need it. These 3 emoji's will help you prioritize your study time and to measure your progress:
- π₯
:egg:
- Understanding this material is required, it covers the base skills you'll need for this module and the next. You do not need to finish all of them but should feel comfortable that you could with enough time. - π£
:hatching_chick:
- Do your best to start this material. you don't need to master it or finish it but getting the main idea will be helpful for taking the next steps. - π₯
:hatched_chick:
- Have you finished all the π₯'s and started all the π£'s? push yourself with these challenges.
There's sooo many examples and exercises in this repository, it's easy to forget of what you still need to finish or what you want to review again. Luckily VSCode is really good at searching through folders of code.
You can write hashtags in your comments while you're studying, then search for those hashtags later so you don't miss anything. Here's some ideas:
// #not-done, still a few blanks left
- search for#not-done
in VScode to find all the exercises you've started and not finished// coercion is confusing, #review this again next week
- search for#review
to find the files you need to study again- ... anything goes! Find the hashtags that work for you
If you create a fork of this repository you can open a project board in your fork to track your progress through the module. Just 3 columns can be enough: Todo, Doing, Done.
What can you expect to learn in this module?
expand/collapse
- Learning from Code
- Reading & understanding other people's code
- Making small, incremental experiments
- Copying & modifying others' code
- Reading and writing unit tests
describe
it
expect(actual).toEqual(expected)
- TDD
- Trust the tests!
- Solving challenges one test at a time
- Debugging
- Stepping through tests in the debugger
- Reading test results to debug code behavior
- Interpreting assertion errors
- Reading and writing documentation
- Code review
Helpful resources for this module
expand/collapse
- JavaScript
- π₯ ./implicit-return
- π₯ ./array-methods: Learn array methods by first understanding the strategies they are designed for.
- π₯ ./ecmascript-modules: Learn the first basics of using
import
andexport
. Just enough to complete this module's group exercises. (week 3) - π£ ./hoisting: That famous thing about JS! It's not so bad once you get it, the key to understanding hoisting is to understand the program life-cycle: creation phase and execution phase. This chapter is more abstract, hoisting is something "invisible" about JavaScript program execution. What's most important is that you are comfortable stepping through code that uses hoisting.
- π£ ./linting: Practice using feedback from ESLint to write code that uses consistent style and best practices. (week 3)
- π₯ ./throw-and-catch: Go deeper into errors and error handling by throwing and catching your own errors.
- π₯ ./prototype-delegation: you do not need to master this! but it will help understand what you see in the debugger, where methods come from, and how to read MDN documentation.
- Practice
- In this repo
- π₯ ./about-testing: A quick introduction to
describe
,it
, andexpect
- π£ ./parsons-problems: Explore behavior, strategy and implementation by reconstructing different solutions to coding challenges.
- π£ ./function-design: Learn a structured approach to understanding and solving coding challenges
- π£ ./fuzz-testing: Write multiple solutions to the same problem, and test your code with random test cases. Using random test cases makes it easier to catch sneaky bugs that you wouldn't think of testing yourself.
- π₯ ./about-testing: A quick introduction to
- Other Repos
- π₯,π£,π₯ Solution Write-Ups Learn to solve coding challenges by studying other people's solutions.
- π£ document-and-pass: write your own solutions and documentation to some coding challenges. Practice debugging and using testing libraries in VSCode
- π£ practice-code-review: (week 3 group exercises)
- In this repo
- π₯: More Practice
- there's many great repos of exercises out there, you just need to find them!
- javascript-practice
- rolling-scopes-school
- Functions and Arrays
- Advanced Collection Methods
- Native Array Methods
- clue
- KBPsystem777
- JavaScript Questions
Are we missing a great set of exercises? Send a PR!
- 30secondsofcode
- javascripttutorial.net: arrays, objects
- minutes-of-javascript
- FunFunFunction:
- the basics
- js-unit-testing-guide (very detailed)
- good test descriptions
- test behavior, not implementation
- FreeCodeCamp: the lingo
- Zach Gollwitzer
- DevSprout
- CodeAcademy (MDN starts at 9:00)
- doesitmutate.xyz
- javascript.info
- FunFun: Map, Reduce Basics
- CYF
- Una Kravets
- Cheat Sheet
- Oliverjam: learn-map-filter, tdd-array-methods
- Reduce
- short, sweet, with a loop (video)
- Sorting an Array of Objects (video)
- another Sorting (video+article)
- Search from Array of Objects (video)
- Create, Update and Loop (article)
- The Coding Train (video)
import
/export
- JavaScript: The Better Parts (first 11 minutes)
- Code Style
- what is programming style?
- why is style important? stackoverflow, Nicholas C. Zakas
- what is a style guide?
- airbnb style guide
- Formatting
- Linting
- what is linting? Envato tuts+, ictshore, (static analysis?)
- Why is linting important? freecodecamp, ibuildmvps, quora
- ESLint sandbox
- ESLint rules
- Linting rules for JSDocs
- in VSCode
- remember all those extensions you installed in the precourse? now they make more sense : )
And last, but not least: Code Review:
- What? Why? Wikipedia, Alex
- How to do code reviews like a human
- The science of code reviews
- Code review on GitHub
- An example code review
- all-about-code-review
You can get by without them, but when they're helpful they're very helpful.
- Interactives
- regex.guide: learn to build regular expressions one question at a time
- regexer.com: realtime regex with a visual breakdown of your regular expression
- Regex Crossword: games to learn regular expressions
- References
The best problem solvers don't start from nothing, they've seen many similar problems before and have learned to adapt other people's code to fit their programs.
This week take your first step to becoming an expert problem solver by studying the many ways people before you have solved common problems.
expand/collapse
- Fork solution-write-ups
- Read through the README, this will be your main exercise for the week.
- Follow the instructions in Getting Started to locally set up your repository.
- Practice running the
sandbox.test.js
file in the example write-up, in VSCode and the browser. (instructions in Getting Started)
- Read through ./about-testing/examples to be familiar with the syntax for
describe
,it
, andexpect
- Create an account on Edabit
In class you will practice writing tests and analyzing solutions using a challenge from Edabit:
Description, Syntax, Test Cases and Use Cases.
- All together:
- look through the examples at the beginning of this README. What is behavior, strategy and implementation?
- go over the README from solution-write-ups
- read through the first 4 sections in the example from solution-write-ups
- In small groups:
- Complete the first 4 sections of the writeup for
Add up the Numbers ...
- You can use the most popular solution solution by _sir to write your tests:
function addUp(num) { return (num * (num + 1)) / 2; }
- Complete the first 4 sections of the writeup for
Strategy, Implementation and Possible Refactors.
-
All together:
- read through the rest of the example in solution-write-ups
-
In small groups:
-
practice writing up 2-3 solutions to the
Add up the Numbers ...
challenge (be sure to test them all!) -
here's a few suggestions to study:
// Gabriel function addUp(num) { let sum = 0; for (let i = 1; i <= num; i++) { sum += i; } return sum; } // λ²λ function addUp(num) { var a = 0; for (var i = num; i > 0; i--) { a += i; } return a; } // doodledob function addUp(num) { x = 0; while (num >= 0) { x += num; num--; } return x; } // _sir function addUp(num) { return (num * (num + 1)) / 2; }
-
Take your time this week to explore other people's code. In the past modules you've studied only a small part of what JS, now you're in the wild! You'll come across all sorts of JS features and new coding styles.
Complete as many write-ups as you can. No need to rush ahead! Take your time studying solutions at your level. It's more helpful to study a variety of solutions than to study a few hard ones:
Learning to read MDN Documentation will help you understand how JS works, and help you do a good write-up:
Practice reconstructing different solutions to the same coding challenge:
- π£ ./parsons-problems
These exercises will help you understand test cases, and be important for next week:
- π£ ./about-testing
Here's some important JS concepts you should start learning about:
- π₯ ./implicit-return: just a different way to write functions
- π£ ./array-methods: +1
- π£ ./hoisting: you do not need to master this! but it will help you understand what's happening in the debugger, some strange bugs, and how
let
/const
&() => {}
are different fromvar
andfunction () {}
- π₯ ./prototype-delegation: you do not need to master this! but it will help understand what you see in the debugger, where methods come from, and how to read MDN documentation.
Learn to write unit tests and to design your own solutions one step at a time.
expand/collapse
- Practice Testing Functions from ./about-testing/exercises
- Read through the steps of Function Design and study the example:
Practice writing unit tests for functions.
- All together: Introduce the syntax for unit testing, and go through the
testing-functions.test.js
example - In small groups: Write some tests!
Practice some ./function-design
- All Together: What is function design? What are the steps? Why is this important?
- In small groups: Write some functions!
Begin studying document-and-pass, and continue with the solution-write-ups. Just like last week, there's no reason to rush yourself. You can learn how to write tests and how to design functions with simple challenges just as well as with hard challenges. So find your level, settle in, and study as many problems as you can.
To help you gain a deeper understanding of how to write and test your own solutions, take some time to study these exercises:
- π₯ ./about-testing
- π£ ./function-design
- π£ ./fuzz-testing
Looking for an extra challenge? try writing and passing tests for a function that throws errors:
- π₯ ./throw-and-catch
Code Quality! Code Review!
In the last two weeks you've explored all the different ways people solve coding problems, and discovered many of the strange things JavaScript is capable of. This week it's time to focus in on best practices. You will learn how to use automated tools to check your own code's quality, and a check-list to review each other's code:
- Generated Documentation: Use jsdoc-to-markdown to convert your JSDoc comments into markdown documentation in a README file.
- Testing: Use unit tests to show that your code does what you think it does.
- Formatting: Use Prettier to format your code, making sure that all the code in your group's repository is formatted the same way and is easy to read.
- Linting: Just because your code runs doesn't mean it's good! Use ESLint to make sure that everyone's code is easy to read, avoids common mistakes, and uses a consistent style. Linting will be the trickiest thing to get used to, it's basically a loud voice saying "NO!" to all sorts of things in your code. But it's for your own good, linting makes it easier to write quality code and to collaborate on a group exercise.
- Code Review: Not everything can be automated, you will also learn how to review each other's code. This week's group exercise comes with at Pull Request template that has a big checklist of everything you need to check. Before you can merge any pull request all of the boxes need to be checked. Also a little annoying at first, but better in the long run.
expand/collapse
Clone the practice-code-review repository and spend some time studying the example solution. You will want to:
- install the repository dependencies
- practice running the solution and the tests
- practice running the code quality scripts
What is Linting? Why is it important?
- All together: Fix a few style mistakes in the ./linting exercises
- In small groups: take a look through the
practice-code-review
repo one someone's local computer. Can you figure out how to run all the code quality scripts?
What is code review? Why is it important?
- All together: Discuss code review, your coach's personal experience, and the code review checklist. (PS. GitHub has great tools for reviewing code in a Pull Request)
- In small groups: Read through the code review checklist and prepare for the week
group exercises
This week's assignment is a group exercise. Your goal isn't to solve as many problems as possible, it's to solve problems as well as possible. You will put extra time into your solutions to make sure everything is perfect - strategy, formatting, linting, testing, and documentation. It will feel like a whole lot of extra work for only small changes, but this is the way of collaboration! Once you get used to it you will see that clean and consistent code saves you lots of time in the long run.
One person in your group should fork practice-code-review, everyone in your group will work from that repository. There is no need to have a plan or a development strategy since you are not building a project - each pull request will be for a stand-alone solution. Practicing code review now will prepare you for the next module when you begin building collaborative JS websites .
## Practice Code Review
- [ ] [Repo]()
- [Project Board]()
- [ ] issues are created with the `new solution` template
- [ ] issues have a label for their challenge and for their strategy
- (no need for milestones)
- [Pull Requests]()
- [ ] every solution is pushed to a separate branch (no pushing to main/master!)
- [ ] every pull request is created with the code-review template
- [ ] every pull request is labeled with the challenge and strategy
- [ ] every box is checked _before_ merging a branch to master/main
- [Retrospective]()
> no need for planning this week. Just open a new issue on the project board each time you start a solution
Take a look at these examples/exercises, they will be helpful:
- π₯ ./linting: Practice using feedback from ESLint to write code that uses consistent style and best practices.
- π£ ./ecmascript-modules: Learn the first basics of using
import
andexport
. Just enough to complete this module's group exercises.
- Students: Here you can find recordings of this module from past classes. Enjoy!
- Do you have a favorite? Send a PR giving it a π
- Coaches: When sending your PR's with links please ...
- Indicate which class you were teaching
- Which week it was (if the module is more than 1 week)
- Give your name
- and a helpful description
- Week 1:
- Sunday Part 1: Reference vs. Value
- Sunday Part 1: Map, Reduce, Filter, Every
- Sunday Part 2: Mini-project intro
- Sunday Part 3: Recap & Homework
- Week 2:
- Sunday Part 1: The Callstack
- Sunday Part 1: Higher Order Functions
- Sunday Part 1: Arrows vs. Functions
- Sunday Part 1: Error: EADDRINUSE
- Sunday Part 2: Reverse-Engineering
- Sunday Part 2: R-E & Assignments
- Week 3:
- Sunday Part 1: Objects 1
- Sunday Part 1: Objects 2
- Sunday Part 2: User input in Objects
- Week 1
- Week 2 - server crash :(
- Week 3: Objects Pt. 1, Objects Pt. 2, Project Intro
- Week 1: solution write-ups, behavior vs. strategy vs. implementation
- Week 2: about testing, function design
- Week 3: code quality & code review By hero Thibault!