diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json index 6b4f5ddfea..4f0d90ad13 100644 --- a/concepts/numbers/.meta/config.json +++ b/concepts/numbers/.meta/config.json @@ -1,5 +1,5 @@ { "blurb": "There are two different kinds of numbers in JavaScript - numbers and bigints. Numbers are for everyday use and are always floating point numbers.", "authors": ["SleeplessByte"], - "contributors": [] + "contributors": ["junedev", "JaPatGitHub"] } diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md index 5f29d773f9..f4b7d596c7 100644 --- a/concepts/numbers/about.md +++ b/concepts/numbers/about.md @@ -28,6 +28,14 @@ There are two built-in objects that are useful when dealing with numbers: - [`Number`][built-in-number]: static properties for common / useful values, static methods for [type-checking][concept-type-checking] and [type-conversion][concept-type-conversion], instance methods for [type-conversion][concept-type-conversion] and [formatting numbers as strings][string-formatting]. - [`Math`][built-in-math]: properties and methods for mathematical constants and functions, does **not** work with `BigInt`. +`Math` also includes methods for rounding numbers. +You can read more about the available rounding options in this [javascript.info article on rounding][ref-math-object-rounding]. + +```javascript +Math.floor(234.34); // => 234 +Math.ceil(234.34); // => 235 +``` + The `Number` built-in global `object` is _also_ a global `function` that can be used to convert _almost anything_ number-like to a `number`. It is less forgiving than _parsing_ a `string` to a `number`. @@ -86,6 +94,7 @@ See [0.30000000000000004.com](https://0.30000000000000004.com/) for a brief expl [comparison]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness [lexical-grammar]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#numeric_literals [string-formatting]: /tracks/javascript/concepts/string-formatting +[ref-math-object-rounding]: https://javascript.info/number#rounding [concept-comparison]: /tracks/javascript/concepts/comparison [concept-type-checking]: /tracks/javascript/concepts/type-checking [concept-type-conversion]: /tracks/javascript/concepts/type-conversion diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md index 07f6172748..0759925f6c 100644 --- a/concepts/numbers/introduction.md +++ b/concepts/numbers/introduction.md @@ -9,3 +9,14 @@ Many programming languages have specific numeric types to represent different ty If you require arbitrary precision or work with extremely large numbers, use the `bigint` type. Otherwise, the `number` type is likely the better option. + +## Rounding + +There is a built-in global object called `Math` that provides various [rounding functions][ref-math-object-rounding]. For example, you can round-down (`floor`) or round-up (`ceil`) decimal numbers to nearest whole numbers. + +```javascript +Math.floor(234.34); // => 234 +Math.ceil(234.34); // => 235 +``` + +[ref-math-object-rounding]: https://javascript.info/number#rounding diff --git a/exercises/concept/freelancer-rates/.docs/hints.md b/exercises/concept/freelancer-rates/.docs/hints.md index b31ff712bb..a4d9d6b400 100644 --- a/exercises/concept/freelancer-rates/.docs/hints.md +++ b/exercises/concept/freelancer-rates/.docs/hints.md @@ -2,17 +2,15 @@ ## 1. Calculate the day rate given an hourly rate -- Use the [numeric operators][ref-numeric-operators] from Lucian's Luscious Lasagna. +- Use the arithmetic operators as mentioned in the introduction of this exercise. -[ref-numeric-operators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators +## 2. Calculate the number of workdays given a budget -## 2. Calculate the month rate, given an hourly rate and a discount +- First determine the day rate, then calculate the number of days, and finally round that number down. -- `100% - discount` equals the percentage after the discount is applied. -- There is a built-in global object called [`Math`][ref-math-object] with functions to, for example, round-down (`floor`) or round-up (`ceil`). +## 3. Calculate the discounted rate for large projects -[ref-math-object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math - -## 3. Calculate the number of workdays given a budget, rate and discount - -- First determine the dayRate, given the discount, then calculate the number of days, and finally round that number down. +- Round down the result from division to get the number of full months. +- `100% - discount` equals the percentage charged after the discount is applied. +- Use `%`, the remainder operator, to calculate the number of days exceeding full months. +- Add the discounted month rates and full day rates and round it up diff --git a/exercises/concept/freelancer-rates/.docs/instructions.md b/exercises/concept/freelancer-rates/.docs/instructions.md index aa8029a8ab..790980e020 100644 --- a/exercises/concept/freelancer-rates/.docs/instructions.md +++ b/exercises/concept/freelancer-rates/.docs/instructions.md @@ -1,19 +1,13 @@ # Instructions -In this exercise you will be writing code to help a freelancer communicate with a project manager by providing a few utility functions to quickly calculate day- and month rates, optionally with a given discount. - -We first establish a few rules between the freelancer and the project manager: - -- The daily rate is 8 times the hourly rate; -- A month has 22 billable days. - -If the freelancer bills the project manager per month or more, there is a discount applied. This can be handy if the project manager has a fixed budget. - -Discounts are modeled as fractional numbers (percentage) between `0.0` (`0%`, no discount) and `0.90` (`90%`, maximum discount). +In this exercise you will be writing code to help a freelancer communicate with his clients about the prices of certain projects. You will write a few utility functions to quickly calculate the costs for the clients. ## 1. Calculate the day rate given an hourly rate -Implement a function to calculate the day rate given an hourly rate: +A client contacts the freelancer to enquire about his rates. +The freelancer explains that he **_works 8 hours a day._** +However, the freelancer knows only his hourly rates for the project. +Help him estimate a day rate given an hourly rate. ```javascript dayRate(89); @@ -22,24 +16,26 @@ dayRate(89); The day rate does not need to be rounded or changed to a "fixed" precision. -## 2. Calculate the month rate, given an hourly rate and a discount +## 2. Calculate the number of workdays given a fixed budget -Implement a function to calculate the month rate, and apply a discount: +Another day, a project manager offers the freelancer to work on a project with a fixed budget. +Given the fixed budget and the freelancer's hourly rate, help him calculate the number of days he would work until the budget is exhausted. +The result _must_ be **rounded down** to the nearest whole number. ```javascript -monthRate(89, 0.42); -// => 9086 +daysInBudget(20000, 89); +// => 28 ``` -The discount is always passed as a number, where `42%` becomes `0.42`. The result _must_ be rounded up to the nearest whole number. - -## 3. Calculate the number of workdays given a budget, rate and discount +## 3. Calculate the discounted rate for large projects -Implement a function that takes a budget, a rate per hour and a discount, and calculates how many full days of work that covers. +Often, the freelancer's clients hire him for projects spanning over multiple months. +In these cases, the freelancer decides to offer a discount for every full month, and the remaining days are billed at day rate. +**_Every month has 22 billable days._** +Help him estimate his cost for such projects, given an hourly rate, the number of days the project spans, and a monthly discount rate. +The discount is always passed as a number, where `42%` becomes `0.42`. The result _must_ be **rounded up** to the nearest whole number. ```javascript -daysInBudget(20000, 89, 0.2002); -// => 35 +priceWithMonthlyDiscount(89, 230, 0.42); +// => 97972 ``` - -The discount is always passed as a `number`. `20.02%` is passed as `0.2002`. The result is the number of days should be rounded down to full days of work. diff --git a/exercises/concept/freelancer-rates/.docs/introduction.md b/exercises/concept/freelancer-rates/.docs/introduction.md index 7171f1948e..2f7b3b0cfa 100644 --- a/exercises/concept/freelancer-rates/.docs/introduction.md +++ b/exercises/concept/freelancer-rates/.docs/introduction.md @@ -12,6 +12,15 @@ Many programming languages have specific numeric types to represent different ty If you require arbitrary precision or work with extremely large numbers, use the `bigint` type. Otherwise, the `number` type is likely the better option. +### Rounding + +There is a built-in global object called `Math` that provides various [rounding functions][ref-math-object-rounding]. For example, you can round-down (`floor`) or round-up (`ceil`) decimal numbers to nearest whole numbers. + +```javascript +Math.floor(234.34); // => 234 +Math.ceil(234.34); // => 235 +``` + ## Arithmetic Operators JavaScript provides 6 different operators to perform basic arithmetic operations on numbers. @@ -72,3 +81,4 @@ y %= 3; // y is now 1 ``` [mdn-operator-precedence]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table +[ref-math-object-rounding]: https://javascript.info/number#rounding diff --git a/exercises/concept/freelancer-rates/.meta/config.json b/exercises/concept/freelancer-rates/.meta/config.json index 42b7139fbe..dfee849c7d 100644 --- a/exercises/concept/freelancer-rates/.meta/config.json +++ b/exercises/concept/freelancer-rates/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "Learn about numbers whilst helping a freelancer communicate with a project manager about day- and month rates.", - "authors": ["SleeplessByte"], - "contributors": [], + "authors": ["SleeplessByte", "JaPatGitHub"], + "contributors": ["junedev"], "files": { "solution": ["freelancer-rates.js"], "test": ["freelancer-rates.spec.js"], diff --git a/exercises/concept/freelancer-rates/.meta/design.md b/exercises/concept/freelancer-rates/.meta/design.md index 6e02f5cc46..2c6decde56 100644 --- a/exercises/concept/freelancer-rates/.meta/design.md +++ b/exercises/concept/freelancer-rates/.meta/design.md @@ -6,6 +6,8 @@ - Know their underlying type (double precision). - Know their basic limitations (not all numbers can be represented, e.g. `0.1 + 0.2`). - Know how to truncate floating point numbers to a certain decimal place (`Math.ceil`, `Math.floor`, `Math.round`) +- Know how to use arithmetic operators (`+`, `-`, `*`, `/`, `%`, `**`) +- Learn about shorthand assignment operators (`+=`, etc.) ## Out of scope @@ -15,6 +17,7 @@ ## Concepts - `numbers` +- `arithmetic-operators` ## Prerequisites diff --git a/exercises/concept/freelancer-rates/.meta/exemplar.js b/exercises/concept/freelancer-rates/.meta/exemplar.js index da707c59cc..2f2959787e 100644 --- a/exercises/concept/freelancer-rates/.meta/exemplar.js +++ b/exercises/concept/freelancer-rates/.meta/exemplar.js @@ -29,39 +29,32 @@ export function dayRate(ratePerHour) { return ratePerHour * 8; } -/** - * Calculates the rate per month - * - * @param {number} ratePerHour - * @param {number} discount for example 20% written as 0.2 - * @returns {number} the rounded up monthly rate - */ -export function monthRate(ratePerHour, discount) { - const monthly = dayRate(ratePerHour) * 22; - const discounted = applyDiscount(monthly, discount); - return Math.ceil(discounted); -} - /** * Calculates the number of days in a budget, rounded down * * @param {number} budget the total budget * @param {number} ratePerHour the rate per hour - * @param {number} discount to apply, example 20% written as 0.2 * @returns {number} the number of days */ -export function daysInBudget(budget, ratePerHour, discount) { - const discounted = applyDiscount(dayRate(ratePerHour), discount); - return Math.floor(budget / discounted); +export function daysInBudget(budget, ratePerHour) { + return Math.floor(budget / dayRate(ratePerHour)); } /** - * Applies a discount to the value + * Calculates the discounted rate for large projects, rounded up * - * @param {number} value - * @param {number} percentage for example 20% written as 0.2 - * @returns {number} the discounted value + * @param {number} ratePerHour + * @param {number} numDays: number of days the project spans + * @param {number} discount: for example 20% written as 0.2 + * @returns {number} the discounted rate, rounded up */ -function applyDiscount(value, percentage) { - return (1 - percentage) * value; +export function priceWithMonthlyDiscount(ratePerHour, numDays, discount) { + const numMonths = Math.floor(numDays / 22); + const monthlyRate = 22 * dayRate(ratePerHour); + const monthlyDiscountedRate = (1 - discount) * monthlyRate; + + const numExtraDays = numDays % 22; + const priceExtraDays = numExtraDays * dayRate(ratePerHour); + + return Math.ceil(numMonths * monthlyDiscountedRate + priceExtraDays); } diff --git a/exercises/concept/freelancer-rates/freelancer-rates.js b/exercises/concept/freelancer-rates/freelancer-rates.js index 31e11d0217..bc8f97f8ca 100644 --- a/exercises/concept/freelancer-rates/freelancer-rates.js +++ b/exercises/concept/freelancer-rates/freelancer-rates.js @@ -29,36 +29,25 @@ export function dayRate(ratePerHour) { throw new Error('Implement the dayRate function'); } -/** - * Calculates the rate per month - * - * @param {number} ratePerHour - * @param {number} discount for example 20% written as 0.2 - * @returns {number} the rounded up monthly rate - */ -export function monthRate(ratePerHour, discount) { - throw new Error('Implement the monthRate function'); -} - /** * Calculates the number of days in a budget, rounded down * - * @param {number} budget the total budget - * @param {number} ratePerHour the rate per hour - * @param {number} discount to apply, example 20% written as 0.2 + * @param {number} budget: the total budget + * @param {number} ratePerHour: the rate per hour * @returns {number} the number of days */ -export function daysInBudget(budget, ratePerHour, discount) { +export function daysInBudget(budget, ratePerHour) { throw new Error('Implement the daysInBudget function'); } /** - * Applies a discount to the value + * Calculates the discounted rate for large projects, rounded up * - * @param {number} value - * @param {number} percentage for example 20% written as 0.2 - * @returns {number} the discounted value + * @param {number} ratePerHour + * @param {number} numDays: number of days the project spans + * @param {number} discount: for example 20% written as 0.2 + * @returns {number} the rounded up discounted rate */ -function applyDiscount(value, percentage) { - throw new Error('Implement the applyDiscount function'); +export function priceWithMonthlyDiscount(ratePerHour, numDays, discount) { + throw new Error('Implement the priceWithMonthlyDiscount function'); } diff --git a/exercises/concept/freelancer-rates/freelancer-rates.spec.js b/exercises/concept/freelancer-rates/freelancer-rates.spec.js index 533fc3e178..2e3026c789 100644 --- a/exercises/concept/freelancer-rates/freelancer-rates.spec.js +++ b/exercises/concept/freelancer-rates/freelancer-rates.spec.js @@ -1,6 +1,10 @@ // @ts-check -import { dayRate, monthRate, daysInBudget } from './freelancer-rates'; +import { + dayRate, + daysInBudget, + priceWithMonthlyDiscount, +} from './freelancer-rates'; const DIFFERENCE_PRECISION_IN_DIGITS = 6; @@ -32,98 +36,57 @@ describe('freelancer rates', () => { }); }); - describe('month rate', () => { - test('at 16/hour', () => { - const actual = monthRate(16, 0); - expect(actual).toBe(2816); - }); - - test('at 25/hour', () => { - const actual = monthRate(25, 0); - expect(actual).toBe(4400); - }); - - test('at 25/hour with a 50% discount', () => { - const actual = monthRate(25, 0.5); - expect(actual).toBe(2200); - }); - - test('at 25/hour with a 1.23% discount', () => { - const actual = monthRate(25, 0.0123); - expect(actual).toBe(4346); - }); - - test('at 31.40/hour with a 5% discount', () => { - const actual = monthRate(31.4, 0.05); - expect(actual).toBe(5251); - }); - - test('at 89.89/hour with a 5% discount', () => { - const actual = monthRate(89.89, 0.05); - expect(actual).toBe(15030); - }); - - test('at 89.89/hour with a 5% discount', () => { - const actual = monthRate(89.89, 0.05); - expect(actual).toBe(15030); - }); - - test('at 97.654321/hour with a 5% discount', () => { - const actual = monthRate(97.654321, 0.05); - expect(actual).toBe(16328); - }); - }); - describe('days in budget', () => { describe('with a budget of 1280', () => { test('at 16/hour', () => { - const actual = daysInBudget(1280, 16, 0); + const actual = daysInBudget(1280, 16); const expected = 10; expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); }); test('at 25/hour', () => { - const actual = daysInBudget(1280, 25, 0); + const actual = daysInBudget(1280, 25); const expected = 6; expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); }); - test('at 25/hour with 30% discount', () => { - const actual = daysInBudget(1280, 25, 0.3); - const expected = 9; + describe('with a budget of 835', () => { + test('at 12/hour', () => { + const actual = daysInBudget(835, 12); + const expected = 8; - expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); + expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); + }); }); }); + }); - describe('with a budget of 10.000', () => { - test('at 25/hour with 5% discount', () => { - const actual = daysInBudget(10000, 25, 0.05); - const expected = 52; - + describe('cost with monthly discount', () => { + describe('at 16/hour', () => { + test('for 70 days', () => { + const actual = priceWithMonthlyDiscount(16, 70, 0); + const expected = 8960; expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); }); - test('at 31.40/hour with 5% discount', () => { - const actual = daysInBudget(10000, 31.4, 0.05); - const expected = 41; - + test('for 130 days with 15% discount', () => { + const actual = priceWithMonthlyDiscount(16, 130, 0.15); + const expected = 14528; expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); }); - - test('at 89.89/hour with 5% discount', () => { - const actual = daysInBudget(10000, 89.89, 0.05); - const expected = 14; - + }); + describe('at 29.654321/hour', () => { + test('for 220 days with 11.2%', () => { + const actual = priceWithMonthlyDiscount(29.654321, 220, 0.112); + const expected = 46347; expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); }); - test('at 97.654321/hour with 5% discount', () => { - const actual = daysInBudget(10000, 97.654321, 0.05); - const expected = 13; - + test('for 155 days with 25.47% discount', () => { + const actual = priceWithMonthlyDiscount(29.654321, 155, 0.3547); + const expected = 23813; expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); }); });