diff --git a/concepts/conditionals-switch/.meta/config.json b/concepts/conditionals-switch/.meta/config.json new file mode 100644 index 0000000000..bf70dc4876 --- /dev/null +++ b/concepts/conditionals-switch/.meta/config.json @@ -0,0 +1,5 @@ +{ + "blurb": "Besides the if-statement, JavaScript also has a switch-statement to conditionally execute logic. It is used when a single variable needs to be compared to multiple variants.", + "authors": ["junedev"], + "contributors": [] +} diff --git a/concepts/conditionals-switch/about.md b/concepts/conditionals-switch/about.md new file mode 100644 index 0000000000..527b31f90f --- /dev/null +++ b/concepts/conditionals-switch/about.md @@ -0,0 +1,85 @@ +# About + +## General Syntax + +Besides the if-statement, JavaScript also has a switch-statement to conditionally execute logic. +It is used when a single variable needs to be compared to multiple variants. +The comparison is done by checking for strict equality (`===`), see [concept comparison][concept-comparison]. +For some variable `x`, the switch statement in JavaScript has the following syntax. + + +```javascript +switch (x) { + case option1: + // code that is executed when "x === option1" is true + break; + case option2: + // code that is executed when "x === option2" is true + break; + default: + // code that is executed when x does not equal any of the options +} +``` + + +The `default` case is optional and used in case you want to execute some code if none of the other options match the variable. + +## Fallthrough by Default + +The `break` statements above are needed because by default all cases are "fallthrough" in JavaScript. +That means without any `break` statement all the code in the cases below the first matching option would be executed even though `x` did not match those options. +This "fallthrough by default" behavior is a common pitfall when using `switch` in JavaScript. +Inside a function, `return` can also be used instead of `break` to avoid this problem. + +You can use the fallthrough behavior to your advantage when you want to apply the same code for multiple cases. +You can find an example of this in the [MDN documentation][mdn-group-cases]. + +## Scope + +By default the variables in the different `case` statements share the same scope. +This can lead to unexpected behavior. +For example due to copying and pasting a case, you could end up with a `let message` declaration in two cases which results in an error, see [MDN documentation][mdn-switch-scope]. +To avoid problems due to the shared scope, you can create a separate scope for each case statement by adding code blocks with curly brackets for each case. + +```javascript +switch (x) { + case option1: { + // Variables declared here are contained to this case. + break; + } + case option2: { + // ... + break; + } + default: { + // ... + } +} +``` + +## Using Expressions + +Instead of a variable `x`, you can also use an expression. +That expression is evaluated once at the beginning of the switch statement and the result compared against the cases. +A common use of this is a "type switch" that executes different code depending on the type of a variable. + + +```javascript +switch (typeof x) { + case 'string': + // code that is executed when x is a string + break; + case 'number': + // code that is executed when x is a number + break; + default: + // code that is executed when x has some other type +} +``` + + +The options can be expressions as well. + +[concept-comparison]: /tracks/javascript/concepts/comparison +[mdn-group-cases]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch#methods_for_multi-criteria_case +[mdn-switch-scope]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch#block-scope_variables_within_switch_statements diff --git a/concepts/conditionals-switch/introduction.md b/concepts/conditionals-switch/introduction.md new file mode 100644 index 0000000000..329b70dbfa --- /dev/null +++ b/concepts/conditionals-switch/introduction.md @@ -0,0 +1,30 @@ +# Introduction + +Besides the if-statement, JavaScript also has a switch-statement to conditionally execute logic. +It is used when a single variable needs to be compared to multiple variants. +The comparison is done by checking for strict equality (`===`), see [concept comparison][concept-comparison]. +For some variable `x`, the switch statement in JavaScript has the following syntax. + + +```javascript +switch (x) { + case option1: + // code that is executed when "x === option1" is true + break; + case option2: + // code that is executed when "x === option2" is true + break; + default: + // code that is executed when x does not equal any of the options +} +``` + + +The `default` case is optional and used when you want to execute some code if none of the other options match the variable. + +The `break` statements above are needed because by default all cases are "fallthrough" in JavaScript. +That means that without any `break` statement all the code in the cases below the first matching option would be executed even though `x` did not match those options. +This "fallthrough by default" behavior is a common pitfall when using `switch` in JavaScript. +Inside a function, `return` can also be used instead of `break` to avoid this problem. + +[concept-comparison]: /tracks/javascript/concepts/comparison diff --git a/concepts/conditionals-switch/links.json b/concepts/conditionals-switch/links.json new file mode 100644 index 0000000000..fe60b4e739 --- /dev/null +++ b/concepts/conditionals-switch/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch", + "description": "MDN: Switch Statement" + } +] diff --git a/concepts/while-loops/.meta/config.json b/concepts/while-loops/.meta/config.json new file mode 100644 index 0000000000..9be7e87218 --- /dev/null +++ b/concepts/while-loops/.meta/config.json @@ -0,0 +1,5 @@ +{ + "blurb": "Execute code repeatedly as long as a certain condition is fulfilled. Depending on when that condition should be checked, you can use a while or a do-while loop in JavaScript.", + "authors": ["junedev"], + "contributors": [] +} diff --git a/concepts/while-loops/about.md b/concepts/while-loops/about.md new file mode 100644 index 0000000000..a1f90c13fe --- /dev/null +++ b/concepts/while-loops/about.md @@ -0,0 +1,89 @@ +# About + +## General Syntax + +With a while loop you can execute code repeatably as long as a certain condition is fulfilled. + +It is written with the `while` keyword followed by a condition wrapped in round brackets and a code block that contains the _body_ of the loop wrapped in curly brackets. + +```javascript +while (condition) { + // code that is executed repeatedly as long as the condition is true +} +``` + +JavaScript also has a do-while loop. +Here the condition is checked after the loop body was executed. +This is useful when the condition depends on evaluations done in the body. + +```javascript +do { + // The code here will always be executed once and then + // repeatedly while the condition is true. +} while (condition); +``` + +## Break + +Inside a loop body you can use the `break` keyword to stop the execution of the loop entirely. +This is often used in combination with `true` as condition. +With that, you can control when the loop should stop from any place inside the loop body. + +```javascript +const winningNumber = 7; + +while (true) { + const num = readUserGuess(); + if (num === winningNumber) { + break; + } +} +``` + +The `break` keyword cannot be used inside a function that is nested in the loop, see the [MDN documentation][mdn-break-in-function] for an example. + +## Continue + +In contrast to `break`, the keyword `continue` only stops the execution of the current iteration and continues with the next one. +With `continue` you can often avoid wrapping big parts of the loop body in an if-statement. + +```javascript +let i = 0; + +while (i < 100) { + i = i + 2; + + if (i % 3 === 0) { + continue; + } + + // The code here will only executed when i was not divisible + // by 3 in the check above. +} +``` + +## Infinite Loops + +A loop that is (theoretically) repeated forever is created when the loop condition is always fulfilled and no break or return statement is reached in the loop body. +The execution has to be terminated from the outside. +Depending on the environment in which such code runs, this will be done automatically or needs a manual intervention. + +```javascript +let i = 0; + +while (i < 100) { + if (i % 3 === 0) { + continue; + } + + i = i + 2; +} + +// This loop runs forever since the variable i does not change +// anymore after it is divisible by 3 the first time. +``` + +Spotting infinite loops might seem trivial in this toy example, but is not always that easy with more complex code. +It is good practice to thoroughly think about whether your condition eventually becomes false or whether your break or return statement is actually reached. + +[mdn-break-in-function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break#break_within_functions diff --git a/concepts/while-loops/introduction.md b/concepts/while-loops/introduction.md new file mode 100644 index 0000000000..4e1a53c802 --- /dev/null +++ b/concepts/while-loops/introduction.md @@ -0,0 +1,40 @@ +# Introduction + +With a while loop you can execute code repeatably as long as a certain condition is fulfilled. +It is written with the `while` keyword followed by a condition wrapped in round brackets and a code block that contains the _body_ of the loop wrapped in curly brackets. + +```javascript +while (condition) { + // code that is executed repeatedly as long as the condition is true +} +``` + +JavaScript also has a do-while loop. +Here the condition is checked after the loop body was executed. +This is useful when the condition depends on the outcome of the code in the body. + +```javascript +do { + // The code here will always be executed once and then + // repeatedly while the condition is true. +} while (condition); +``` + +Inside a loop body you can use the `break` keyword to stop the execution of the loop entirely. +In contrast to this, the keyword `continue` only stops the execution of the current iteration and continues with the next one. +With `continue` you can often avoid wrapping big parts of the loop body in an if-statement. + +```javascript +let i = 0; + +while (i < 100) { + i = i + 2; + + if (i % 3 === 0) { + continue; + } + + // The code here will only executed when i was not divisible + // by 3 in the check above. +} +``` diff --git a/concepts/while-loops/links.json b/concepts/while-loops/links.json new file mode 100644 index 0000000000..5071cdf5d6 --- /dev/null +++ b/concepts/while-loops/links.json @@ -0,0 +1,18 @@ +[ + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while", + "description": "MDN: While Loop" + }, + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while", + "description": "MDN: Do-While Loop" + }, + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break", + "description": "MDN: break" + }, + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue", + "description": "MDN: continue" + } +] diff --git a/config.json b/config.json index 7518c3cd2d..2aca3a6515 100644 --- a/config.json +++ b/config.json @@ -157,6 +157,14 @@ "concepts": ["for-loops", "increment-decrement"], "prerequisites": ["arrays", "comparison", "conditionals"], "status": "beta" + }, + { + "slug": "mixed-juices", + "name": "Mixed Juices", + "uuid": "81c3fb86-af86-4c56-a45f-e021403c4070", + "concepts": ["while-loops", "conditionals-switch"], + "prerequisites": ["comparison", "conditionals", "arrays"], + "status": "beta" } ], "practice": [ @@ -1633,6 +1641,16 @@ "uuid": "a2347a19-1e44-449b-9741-94eda00d8ba7", "slug": "increment-decrement", "name": "Increment and Decrement Operators" + }, + { + "uuid": "4e68e39a-e36c-4d2d-8714-eb6482e31ff5", + "slug": "while-loops", + "name": "While Loops" + }, + { + "uuid": "d5d54931-b2a7-4b1a-a593-ad85a2810f2f", + "slug": "conditionals-switch", + "name": "Switch Statement" } ], "key_features": [ diff --git a/exercises/concept/mixed-juices/.docs/hints.md b/exercises/concept/mixed-juices/.docs/hints.md new file mode 100644 index 0000000000..60f25bc340 --- /dev/null +++ b/exercises/concept/mixed-juices/.docs/hints.md @@ -0,0 +1,30 @@ +# Hints + +## 1. Determine how long it takes to mix a juice + +- Set up a [switch statement][mdn-switch] for the `name` variable. +- The different cases should represent the different juice names. +- Use the `default` case to cover all other names. +- Remember that the cases are [fallthrough by default][mdn-fallthrough] so make sure you did something to prevent that behavior. + +## 2. Replenish the lime wedge supply + +- Use a [while loop][mdn-while] to cut one lime after the other. +- Revisit the [arrays concept][concept-arrays] to find a way to remove the limes from the list in the correct order. +- Set up a [switch statement][mdn-switch] to get the number of wedges for a certain size of a lime. +- You need to keep track of two things, how many limes where already cut and how many wedges are still missing. +- You can combine two conditions for the loop using [logical operators][concept-booleans]. + +## 3. Finish up the shift + +- Use a [do-while loop][mdn-do-while] to handle one order after the other. +- Revisit the [arrays concept][concept-arrays] to find a way to remove the drinks from the list in the correct order. +- You already have a function that determines the time it takes to prepare a drink, use it to reduce the time that is left accordingly. +- You can combine two conditions for the loop using [logical operators][concept-booleans]. + +[mdn-switch]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch# +[mdn-fallthrough]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch#what_happens_if_i_forgot_a_break +[mdn-while]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while +[mdn-do-while]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while +[concept-booleans]: /tracks/javascript/concepts/booleans +[concept-arrays]: /tracks/javascript/concepts/arrays diff --git a/exercises/concept/mixed-juices/.docs/instructions.md b/exercises/concept/mixed-juices/.docs/instructions.md new file mode 100644 index 0000000000..ede26723e9 --- /dev/null +++ b/exercises/concept/mixed-juices/.docs/instructions.md @@ -0,0 +1,60 @@ +# Instructions + +Your friend Li Mei runs her own juice bar where she sells delicious mixed fruit juices. +You are a frequent customer in her shop and realized you could make your friend's life easier. +You decide to use your coding skills to help Li Mei with her job. + +## 1. Determine how long it takes to mix a juice + +Li Mei likes to tell her customers in advance how long they have to wait for a juice from the menu that they ordered. +She has a hard time remembering the exact numbers, because the time it takes to mix the juices varies. +`'Pure Strawberry Joy'` takes 0.5 minutes, `'Energizer'` and `'Green Garden'` take 1.5 minutes each, `'Tropical Island'` takes 3 minutes and `'All or Nothing'` takes 5 minutes. +For all other drinks (e.g., special offers) you can assume a preparation time of 2.5 minutes. + +To help your friend, write a function `timeToMixJuice` that takes a juice from the menu as an argument and returns the number of minutes it take to mix that drink. + +```javascript +timeToMixJuice('Tropical Island'); +// => 3 + +timeToMixJuice('Berries & Lime'); +// => 2.5 +``` + +## 2. Replenish the lime wedge supply + +A lot of Li Mei's creations include lime wedges, either as an ingredient or as part of the decoration. +So when she starts her shift in the morning she needs to make sure the bin of lime wedges is full for the day ahead. + +Implement the function `limesToCut` which takes the number of lime wedges Li Mei needs to cut and an array representing the supply of whole limes she has at hand. +She can get 6 wedges from a `'small'` lime, 8 wedges from a `'medium'` lime and 10 from a `'large'` lime. +She always cuts the limes in the order in which they appear in the list, starting with the first item. +She keeps going until she reached the number of wedges that she needs or until she runs out of limes. + +Li Mei would like to know in advance how many limes she needs to cut. +The `limesToCut` function should return the number of limes to cut. + +```javascript +limesToCut(25, ['small', 'small', 'large', 'medium', 'small']); +// => 4 +``` + +## 3. Finish up the shift + +Li Mei always works until 3pm. +Then her employee Dmitry takes over. +There are often drinks that have been ordered but are not prepared yet when Li Mei's shift ends. +Dmitry will then prepare the remaining juices. + +To make the hand-over easier, implement a function `remainingOrders` which takes the number of minutes left in Li Mei's shift and an array of juices that have been ordered but not prepared yet. +The function should return the orders that Li Mei cannot start preparing before the end of her work day. + +The time left in the shift will always be greater than 0. +Furthermore the orders are prepared in the order in which they appear in the array. +If Li Mei starts to mix a certain juice, she will always finish it even if she has to work a bit longer. +If there are no remaining orders left that Dmitry needs to take care of, an empty array should be returned. + +```javascript +remainingOrders(5, ['Energizer', 'All or Nothing', 'Green Garden']); +// => ['Green Garden'] +``` diff --git a/exercises/concept/mixed-juices/.docs/introduction.md b/exercises/concept/mixed-juices/.docs/introduction.md new file mode 100644 index 0000000000..16a6bf7de0 --- /dev/null +++ b/exercises/concept/mixed-juices/.docs/introduction.md @@ -0,0 +1,73 @@ +# Introduction + +## While Loops + +With a while loop you can execute code repeatably as long as a certain condition is fulfilled. +It is written with the `while` keyword followed by a condition wrapped in round brackets and a code block that contains the _body_ of the loop wrapped in curly brackets. + +```javascript +while (condition) { + // code that is executed repeatedly as long as the condition is true +} +``` + +JavaScript also has a do-while loop. +Here the condition is checked after the loop body was executed. +This is useful when the condition depends on evaluations done in the body. + +```javascript +do { + // The code here will always be executed once and then + // repeatedly while the condition is true. +} while (condition); +``` + +Inside a loop body you can use the `break` keyword to stop the execution of the loop entirely. +In contrast to this, the keyword `continue` only stops the execution of the current iteration and continues with the next one. +With `continue` you can often avoid wrapping big parts of the loop body in an if-statement. + +```javascript +let i = 0; + +while (i < 100) { + i = i + 2; + + if (i % 3 === 0) { + continue; + } + + // The code here will only executed when i was not divisible + // by 3 in the check above. +} +``` + +## Switch Statements + +Besides the if-statement, JavaScript also has a switch-statement to conditionally execute logic. +It is used when a single variable needs to be compared to multiple variants. +The comparison is done by checking for strict equality (`===`), see [concept comparison][concept-comparison]. +For some variable `x`, the switch statement in JavaScript has the following syntax. + + +```javascript +switch (x) { + case option1: + // code that is executed when "x === option1" is true + break; + case option2: + // code that is executed when "x === option2" is true + break; + default: + // code that is executed when x does not equal any of the options +} +``` + + +The `default` case is optional and used in case you want to execute some code if none of the other options match the variable. + +The `break` statements above are needed because by default all cases are "fallthrough" in JavaScript. +That means without any `break` statement all the code in the cases below the first matching option would be executed even though `x` did not match those options. +This "fallthrough by default" behavior is a common pitfall when using `switch` in JavaScript. +Inside a function, `return` can also be used instead of `break` to avoid this problem. + +[concept-comparison]: /tracks/javascript/concepts/comparison diff --git a/exercises/concept/mixed-juices/.eslintrc b/exercises/concept/mixed-juices/.eslintrc new file mode 100644 index 0000000000..b8dab20247 --- /dev/null +++ b/exercises/concept/mixed-juices/.eslintrc @@ -0,0 +1,29 @@ +{ + "root": true, + "parser": "babel-eslint", + "parserOptions": { + "ecmaVersion": 7, + "sourceType": "module" + }, + "globals": { + "BigInt": true + }, + "env": { + "es6": true, + "node": true, + "jest": true + }, + "extends": [ + "eslint:recommended", + "plugin:import/errors", + "plugin:import/warnings" + ], + "rules": { + "linebreak-style": "off", + + "import/extensions": "off", + "import/no-default-export": "off", + "import/no-unresolved": "off", + "import/prefer-default-export": "off" + } +} diff --git a/exercises/concept/mixed-juices/.gitignore b/exercises/concept/mixed-juices/.gitignore new file mode 100644 index 0000000000..bdb912f98a --- /dev/null +++ b/exercises/concept/mixed-juices/.gitignore @@ -0,0 +1,3 @@ +node_modules +yarn-error.log + diff --git a/exercises/concept/mixed-juices/.meta/config.json b/exercises/concept/mixed-juices/.meta/config.json new file mode 100644 index 0000000000..5581bc9024 --- /dev/null +++ b/exercises/concept/mixed-juices/.meta/config.json @@ -0,0 +1,11 @@ +{ + "blurb": "Help your friend Li Mei run her juice bar with your knowledge of while loops and switch statements.", + "authors": ["junedev"], + "contributors": [], + "files": { + "solution": ["mixed-juices.js"], + "test": ["mixed-juices.spec.js"], + "exemplar": [".meta/exemplar.js"] + }, + "forked_from": ["swift/master-mixologist"] +} diff --git a/exercises/concept/mixed-juices/.meta/design.md b/exercises/concept/mixed-juices/.meta/design.md new file mode 100644 index 0000000000..b073997301 --- /dev/null +++ b/exercises/concept/mixed-juices/.meta/design.md @@ -0,0 +1,90 @@ +# Design + +## Learning objectives + +- What does a while loop do +- What is the difference to do-while +- Syntax `while(){}` and `do{} while()` +- Break and continue +- What is the switch statement +- Syntax `switch(){}` +- What is the `default` case for +- What does `break` do +- Why is break so important when using switch + +## Out of Scope + +The following topics are out of scope because they are covered by another concept exercise. + +- For loops +- Array loops (for...in, forEach, map) + +## Concepts + +The Concepts this exercise unlocks are: + +- `while-loops` +- `conditionals-switch` + +## Prerequisites + +- `comparison` for writing the condition in the loop header +- `conditionals` because they introduced the student to the concept of conditional execution +- `arrays` because they are used to loop over them in the exercise + +## Analyzer + +This exercise could benefit from the following rules in the [analyzer][analyzer]. +The comment types mentioned below only serve as a proposal. + +1. `timeToMixJuice` + + - `essential`: Verify the student used a switch statement. + Would be nice if we could give different feedback depending on what the student used instead. + If it was if-else, comment that switch is better suited for so many different variants. + If an object was used, comment that this is nice but the goal is to practice switch. + - `essential`: Verify that there are 5 cases and a default case in the switch statement to make sure the student did not tailor their code for the test cases (e.g., used more cases instead of default). + - `actionable`: If the student used `break`, comment to use early `return`s instead to avoid assigning to a helper variable first and then returning that variable. + - `celebratory`: Comment something nice when a student used grouped case statements. + ```javascript + case 'Energizer': + case 'Green Garden': + return 1.5; + ``` + +2. `limesToCut` + + - A solution that uses `if (limes.length < 0) break;` instead of combining the conditions should be considered equally correct to the exemplar solution. + The version in the examplar file is shorter but the break version emphasizes that there is a special edge case. + - `essential`: Verify that `while` was used. + - `essential`: If a helper function was used for the switch statement, check that is was not exported. + - `actionable`: If the student wrote `if (limes.length < 0) return limesCut`, comment that the duplication of `return limesCut` can be avoided by using break there instead of return. + - `actionable`: Tell the student to use a helper function to wrap the switch statement for readability if he/she did not do that. + - `informative`: If the student used a counter to iterate over the array, show a comment about about `shift`. + - `informative`: Remind the student about `++` if it was not used to increment the lime counter. + - `informative`: Check whether a shorthand assignment `+=` was used to increase the loop counter. + - `informative`: If `default` was included in the switch statement, remind the student that it is optional and not needed in the scope of the task. + - `celebratory`: Make a positive remark if the student used a helper function to wrap the switch statement. + - `celebratory`: Celebrate if the student used `++` and `+=`. + +3. `remainingOrders` + + - `essential`: Verify that do-while was used. + If while was used instead, say that do-while is a better fit because there is always at least one iteration (because `timeLeft` is always > 0) and the condition can best be checked after running the code. + - `essential`: Verify `timeToMixJuice` was reused instead of duplicating the code. + - Most of the points from task 2 also apply here. + Check what can be reused. + +## Notes + +The exercise is inspired by the [Master Mixologist Exercise in the Swift track][swift-master-mixologist]. +The original exercise also included for loops which is covered by a different exercise in the JavaScript track. +The tasks were adapted accordingly which also simplified them. +The alcohol was replaced and the name was changed to match the new story. + +## Improvement + +The exercise would benefit from another task to practice `continue`. + +[analyzer]: https://github.com/exercism/javascript-analyzer +[swift-master-mixologist]: https://github.com/exercism/swift/blob/main/exercises/concept/master-mixologist/.docs/instructions.md diff --git a/exercises/concept/mixed-juices/.meta/exemplar.js b/exercises/concept/mixed-juices/.meta/exemplar.js new file mode 100644 index 0000000000..8aa1472a67 --- /dev/null +++ b/exercises/concept/mixed-juices/.meta/exemplar.js @@ -0,0 +1,79 @@ +// @ts-check +// +// The line above enables type checking for this file. Various IDEs interpret +// the @ts-check directive. It will give you helpful autocompletion when +// implementing this exercise. + +/** + * Determines how long it takes to prepare a certain juice. + * + * @param {string} name + * @returns {number} time in minutes + */ +export function timeToMixJuice(name) { + switch (name) { + case 'Pure Strawberry Joy': + return 0.5; + case 'Energizer': + return 1.5; + case 'Green Garden': + return 1.5; + case 'Tropical Island': + return 3; + case 'All or Nothing': + return 5; + default: + return 2.5; + } +} + +/** + * Calculates the number of limes that need to be cut + * to reach a certain supply. + * + * @param {number} maxWedges + * @param {string[]} limes + * @returns {number} number of limes cut + */ +export function limesToCut(wedgesNeeded, limes) { + let limesCut = 0; + while (wedgesNeeded > 0 && limes.length > 0) { + limesCut++; + wedgesNeeded -= wedgesFromLime(limes.shift()); + } + + return limesCut; +} + +/** + * Determines the number of wedges that can be cut + * from a lime of the given size. + * + * @param {string} size + * @returns number of wedges + */ +function wedgesFromLime(size) { + switch (size) { + case 'small': + return 6; + case 'medium': + return 8; + case 'large': + return 10; + } +} + +/** + * Determines which juices still need to be prepared after the end of the shift. + * + * @param {number} timeLeft + * @param {string[]} orders + * @returns {string[]} remaining orders after the time is up + */ +export function remainingOrders(timeLeft, orders) { + do { + timeLeft -= timeToMixJuice(orders.shift()); + } while (timeLeft > 0 && orders.length > 0); + + return orders; +} diff --git a/exercises/concept/mixed-juices/.npmrc b/exercises/concept/mixed-juices/.npmrc new file mode 100644 index 0000000000..d26df800bb --- /dev/null +++ b/exercises/concept/mixed-juices/.npmrc @@ -0,0 +1 @@ +audit=false diff --git a/exercises/concept/mixed-juices/LICENSE b/exercises/concept/mixed-juices/LICENSE new file mode 100644 index 0000000000..a7527f968f --- /dev/null +++ b/exercises/concept/mixed-juices/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Exercism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/exercises/concept/mixed-juices/babel.config.js b/exercises/concept/mixed-juices/babel.config.js new file mode 100644 index 0000000000..5cec97251a --- /dev/null +++ b/exercises/concept/mixed-juices/babel.config.js @@ -0,0 +1,15 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 'current', + }, + useBuiltIns: 'entry', + corejs: 3, + }, + ], + ], + plugins: ['@babel/plugin-syntax-bigint'], +}; diff --git a/exercises/concept/mixed-juices/mixed-juices.js b/exercises/concept/mixed-juices/mixed-juices.js new file mode 100644 index 0000000000..dde696fac8 --- /dev/null +++ b/exercises/concept/mixed-juices/mixed-juices.js @@ -0,0 +1,38 @@ +// @ts-check +// +// The line above enables type checking for this file. Various IDEs interpret +// the @ts-check directive. It will give you helpful autocompletion when +// implementing this exercise. + +/** + * Determines how long it takes to prepare a certain juice. + * + * @param {string} name + * @returns {number} time in minutes + */ +export function timeToMixJuice(name) { + throw new Error('Please implement the timeToMixJuice function'); +} + +/** + * Calculates the number of limes that need to be cut + * to reach a certain supply. + * + * @param {number} wedgesNeeded + * @param {string[]} limes + * @returns {number} number of limes cut + */ +export function limesToCut(wedgesNeeded, limes) { + throw new Error('Please implement the limesToCut function'); +} + +/** + * Determines which juices still need to be prepared after the end of the shift. + * + * @param {number} timeLeft + * @param {string[]} orders + * @returns {string[]} remaining orders after the time is up + */ +export function remainingOrders(timeLeft, orders) { + throw new Error('Please implement the remainingOrders function'); +} diff --git a/exercises/concept/mixed-juices/mixed-juices.spec.js b/exercises/concept/mixed-juices/mixed-juices.spec.js new file mode 100644 index 0000000000..673650a9ec --- /dev/null +++ b/exercises/concept/mixed-juices/mixed-juices.spec.js @@ -0,0 +1,113 @@ +import { timeToMixJuice, limesToCut, remainingOrders } from './mixed-juices.js'; + +describe('timeToMixJuice', () => { + test(`returns the correct time for 'Pure Strawberry Joy'`, () => { + expect(timeToMixJuice('Pure Strawberry Joy')).toBe(0.5); + }); + + test('returns the correct times for all other standard menu items', () => { + expect(timeToMixJuice('Energizer')).toBe(1.5); + expect(timeToMixJuice('Green Garden')).toBe(1.5); + expect(timeToMixJuice('Tropical Island')).toBe(3); + expect(timeToMixJuice('All or Nothing')).toBe(5); + }); + + test('returns the same time for all other juices', () => { + const defaultTime = 2.5; + expect(timeToMixJuice('Limetime')).toBe(defaultTime); + expect(timeToMixJuice('Manic Organic')).toBe(defaultTime); + expect(timeToMixJuice('Papaya & Peach')).toBe(defaultTime); + }); +}); + +describe('limesToCut', () => { + test('calculates the number of limes needed to reach the target supply', () => { + const limes = [ + 'small', + 'large', + 'large', + 'medium', + 'small', + 'large', + 'large', + 'medium', + ]; + expect(limesToCut(42, limes)).toBe(6); + + expect(limesToCut(4, ['medium', 'small'])).toBe(1); + }); + + test('uses up all limes if there are not enough to reach the target', () => { + const limes = [ + 'small', + 'large', + 'large', + 'medium', + 'small', + 'large', + 'large', + ]; + + expect(limesToCut(80, limes)).toBe(7); + }); + + test('if no new wedges are needed, no limes are cut', () => { + expect(limesToCut(0, ['small', 'large', 'medium'])).toBe(0); + }); + + test('works if no limes are available', () => { + expect(limesToCut(10, [])).toBe(0); + }); +}); + +describe('remainingOrders', () => { + test('correctly determines the remaining orders', () => { + const orders = [ + 'Tropical Island', + 'Energizer', + 'Limetime', + 'All or Nothing', + 'Pure Strawberry Joy', + ]; + const expected = ['All or Nothing', 'Pure Strawberry Joy']; + + expect(remainingOrders(7, orders)).toEqual(expected); + }); + + test('correctly handles orders that were started because there was time left', () => { + const orders = [ + 'Pure Strawberry Joy', + 'Pure Strawberry Joy', + 'Vitality', + 'Tropical Island', + 'All or Nothing', + 'All or Nothing', + 'All or Nothing', + 'Green Garden', + 'Limetime', + ]; + const expected = ['All or Nothing', 'Green Garden', 'Limetime']; + + expect(remainingOrders(13, orders)).toEqual(expected); + }); + + test('counts all orders as fulfilled if there is enough time', () => { + const orders = [ + 'Energizer', + 'Green Garden', + 'Ruby Glow', + 'Pure Strawberry Joy', + 'Tropical Island', + 'Limetime', + ]; + + expect(remainingOrders(12, orders)).toEqual([]); + }); + + test('works if there is only very little time left', () => { + const orders = ['Bananas Gone Wild', 'Pure Strawberry Joy']; + const expected = ['Pure Strawberry Joy']; + + expect(remainingOrders(0.2, orders)).toEqual(expected); + }); +}); diff --git a/exercises/concept/mixed-juices/package.json b/exercises/concept/mixed-juices/package.json new file mode 100644 index 0000000000..50cf5678d3 --- /dev/null +++ b/exercises/concept/mixed-juices/package.json @@ -0,0 +1,32 @@ +{ + "name": "@exercism/javascript-mixed-juices", + "description": "Exercism concept exercise on while loops and switch statements", + "author": "Franziska Obbarius <12543047+junedev@users.noreply.github.com>", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/exercism/javascript", + "directory": "exercises/concept/mixed-juices" + }, + "devDependencies": { + "@babel/cli": "^7.13.14", + "@babel/core": "^7.13.15", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/preset-env": "^7.13.15", + "@types/jest": "^26.0.22", + "@types/node": "^14.14.37", + "babel-eslint": "^10.1.0", + "babel-jest": "^26.6.3", + "core-js": "^3.10.1", + "eslint": "^7.23.0", + "eslint-plugin-import": "^2.22.1", + "jest": "^26.6.3" + }, + "scripts": { + "test": "jest --no-cache ./*", + "watch": "jest --no-cache --watch ./*", + "lint": "eslint ." + }, + "license": "MIT", + "dependencies": {} +}