diff --git a/concepts/comparison/about.md b/concepts/comparison/about.md new file mode 100644 index 0000000000..07b750922d --- /dev/null +++ b/concepts/comparison/about.md @@ -0,0 +1,44 @@ +# About + +TODO: this is a stub that needs more information + +## Comparing numbers + +Numbers are considered equal if they have the same value. + +```javascript +1 == 1.0; +// => true + +1 === 1.0; +// => true +// Remember, all numbers are floating-points, so this is different syntax for +// the exact same value. + +1 === 1n; +// => false +// Strictly checking a number against a bigint will always result in false. + +1 == 1n; +// => true +// A number is equal to a bigint if they represent the same value. + +1.0 == 1n; +// => true +// A number is equal to a bigint if they represent the same value. +``` + +There are different outcomes when comparing numbers with different types. +In general, only two operands of the type `number` can ever be _strictly equal_ (`===`), and the following can be used for _loose equality_ (`==`): + +| A | B | `==` | +| ------ | --------- | --------------------- | +| Number | Undefined | `false` | +| Number | Null | `false` | +| Number | Number | `A === B` | +| Number | String | `A === ToNumber(B)` | +| Number | Boolean | `A === ToNumber(B)` | +| Number | Object | `A == ToPrimitive(B)` | + +- `ToNumber(X)` attempts to convert its argument `X` to a `number` before comparison. It is equivalent to `+B` (the unary `+` operator). +- `ToPrimitive(X)` attempts to convert its object argument `X` to a primitive value, by attempting to invoke varying sequences of `X.toString` and `X.valueOf` methods on `X`. diff --git a/concepts/comparison/introduction.md b/concepts/comparison/introduction.md new file mode 100644 index 0000000000..e10b99d013 --- /dev/null +++ b/concepts/comparison/introduction.md @@ -0,0 +1 @@ +# Introduction diff --git a/concepts/comparison/links.json b/concepts/comparison/links.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/concepts/comparison/links.json @@ -0,0 +1 @@ +[] diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md index e5e2c37bd1..5b5b2eae6b 100644 --- a/concepts/numbers/about.md +++ b/concepts/numbers/about.md @@ -1,3 +1,97 @@ # About -TODO: add information on numbers concept +There are two different kinds of numbers in JavaScript - numbers and "bigints" + +Numbers are the most used, and represent numeric data type in the double-precision 64-bit floating point format. + +- `number`: a numeric data type in the double-precision 64-bit floating point format (IEEE 754). + Examples are `-6`, `-2.4`, `0`, `0.1`, `1`, `3.14`, `16.984025`, `25`, `976`, `1024.0` and `500000`. +- `bigint`: a numeric data type that can represent _integers_ in the arbitrary precision format. + Examples are `-12n`, `0n`, `4n`, and `9007199254740991n`. + +```javascript +let numericValue = 42; +// => 42 +``` + +A number literal like `42` in JavaScript code is a floating-point value, not an integer. +There is no separate integer type in common everyday use. +The `bigint` type is not designed to replace the `number` type for everyday uses. +`42` is still a `Number`, not a `BigInt`. + +Number may also be expressed in literal forms like `0b101`, `0o13`, `0x0A`. Learn more on numeric lexical grammar [here][lexical-grammar]. + +## Built-in Object + +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][type-checking] and [type-conversion][type-conversion], instance methods for [type-conversion][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`. + +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`. + +```javascript +const date = new Date('December 17, 1995 03:24:00'); +const unix = Number(date); + +unix; +// => 819199440000 +``` + +There are three types of maximum (and minimum / maximum negative) values for numbers in JavaScript: + +- `VALUE`: given by `Number.MAX_VALUE` and `Number.MIN_VALUE` +- `INFINITY`: given by `Number.POSITIVE_INFINITY` and `Number.NEGATIVE_INFINITY` +- `SAFE_INTEGER`: given by `Number.MAX_SAFE_INTEGER` and `Number.MIN_SAFE_INTEGER` + +Because of how numbers in JavaScript are implemented, **not** every number between `Number.MIN_VALUE` and `Number.MAX_VALUE` can be represented. +However, _every_ number between `Number.MIN_SAFE_INTEGER - 1` and `Number.MAX_SAFE_INTEGER + 1` **can** be represented. + +## Comparison + +Numbers are considered equal if they have the same value. + +```javascript +1 == 1.0; +// => true + +1 === 1.0; +// => true +// Remember, all numbers are floating-points, so this is different syntax for +// the exact same value. + +1 === 1n; +// => false +// Strictly checking a number against a bigint will always result in false. +``` + +See [comparison][concept-comparison] for more information on comparisons in general and comparing numeric values in JavaScript. + +## Pitfalls + +Becuase numbers in JavaScript are floating point numbers, all math using these values is floating point math. Therefore, in JavaScript: + +```javascript +0.1 + 0.2 === 0.3; +// => false +``` + +See [0.30000000000000004.com](https://0.30000000000000004.com/) for a brief explanation and [Appendix D](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) of Oracle's Numerical Computation Guide "What Every Computer Scientist Should Know About Floating-Point Arithmetic" for an in depth explanation. + +## Related concepts + + + +[concept:javascript/comparison](https://github.com/exercism/javascript/tree/main/concepts/comparison) [concept:javascript/type-checking](https://github.com/exercism/javascript/tree/main/concepts/type-checking) [concept:javascript/type-conversion](https://github.com/exercism/javascript/tree/main/concepts/type-conversion) [concept:javascript/string-formatting](https://github.com/exercism/javascript/tree/main/concepts/string-formatting) + +[built-in-number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number +[built-in-math]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math +[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]: https://exercism.lol/tracks/javascript/concepts/string-formating +[concept-comparison]: .. +[concept-type-checking]: https://exercism.lol/tracks/javascript/concepts/type-checking +[concept-type-conversion]: https://exercism.lol/tracks/javascript/concepts/type-conversion diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md index fcab3fc3f9..07f6172748 100644 --- a/concepts/numbers/introduction.md +++ b/concepts/numbers/introduction.md @@ -1,15 +1,11 @@ # Introduction -There are two different types of numbers in JavaScript: +Many programming languages have specific numeric types to represent different types of numbers, but JavaScript only has two: -- `number`: a numeric data type in the double-precision 64-bit floating point - format (IEEE 754). Examples are `-6`, `-2.4`, `0`, `0.1`, `1`, `3.14`, - `16.984025`, `25`, `976`, `1024.0` and `500000`. -- `bigint`: a numeric data type that can represent _integers_ in the arbitrary - precision format. Examples are `-12n`, `0n`, `4n`, and `9007199254740991n`. +- `number`: a numeric data type in the double-precision 64-bit floating point format (IEEE 754). + Examples are `-6`, `-2.4`, `0`, `0.1`, `1`, `3.14`, `16.984025`, `25`, `976`, `1024.0` and `500000`. +- `bigint`: a numeric data type that can represent _integers_ in the arbitrary precision format. + Examples are `-12n`, `0n`, `4n`, and `9007199254740991n`. -In contrast, in many other programming languages different numeric types can exist, -for example: Integers, Floats, Doubles, or Bignums. - -If you require arbitrary precision or work with extremely large numbers, use the -`bigint` type. Otherwise, the `number` type is likely the better option. +If you require arbitrary precision or work with extremely large numbers, use the `bigint` type. +Otherwise, the `number` type is likely the better option. diff --git a/concepts/numbers/links.json b/concepts/numbers/links.json index fe51488c70..cbda6acd9c 100644 --- a/concepts/numbers/links.json +++ b/concepts/numbers/links.json @@ -1 +1,18 @@ -[] +[ + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number", + "description": "MDN: Number (Built-in object)" + }, + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math", + "description": "MDN: Math (Built-in object)" + }, + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#numeric_literals", + "description": "MDN: Lexical grammar for numeric literals" + }, + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness", + "description": "MDN: Equality comparisons and sameness" + } +] diff --git a/config.json b/config.json index 14ec1788e9..07d85fc36e 100644 --- a/config.json +++ b/config.json @@ -68,7 +68,8 @@ "status": "active" }, { - "slug": "numbers", + "slug": "freelancer-rates", + "name": "Freelancer Rates", "uuid": "0aff2fa7-55ea-47e9-af4a-78927d916baf", "concepts": ["numbers"], "prerequisites": ["basics"], @@ -1554,7 +1555,7 @@ "uuid": "8da586c3-9327-46e2-ad32-c8423061912d", "slug": "numbers", "name": "Numbers", - "blurb": "TODO: add blurb for numbers concept" + "blurb": "There are two different kinds of numbers in JavaScript - numbers and bigints. Numbers are for everyday use and are always floating point numbers." }, { "uuid": "b3aa57d9-74b2-4d04-a673-ae2402630d8b", diff --git a/exercises/concept/freelancer-rates/.docs/hints.md b/exercises/concept/freelancer-rates/.docs/hints.md new file mode 100644 index 0000000000..5b5e27aef9 --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/hints.md @@ -0,0 +1,18 @@ +# Hints + +## 1. Calculate the day rate given an hourly rate + +- Use the [numeric operators][ref-numeric-operators] from Lucian's Lucious Lasagna. + +[ref-numeric-operators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators + +## 2. Calculate the month rate, given an hourly rate and a discount + +- `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`). + +[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. diff --git a/exercises/concept/freelancer-rates/.docs/instructions.md b/exercises/concept/freelancer-rates/.docs/instructions.md new file mode 100644 index 0000000000..c18fd5701d --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/instructions.md @@ -0,0 +1,47 @@ +# 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, there is a discount applied. This can be handy if the project manager has a fixed budget. + +Discounts are modeled as fractional numbers followed by a `%` (percentage) between `0.0%` (no discount) and `90.0%` (maximum discount). + +## Tasks + +## 1. Calculate the day rate given an hourly rate + +Implement a function to calculate the day rate given an hourly rate: + +```javascript +dayRate(89); +// => 712 +``` + +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 + +Implement a function to calculate the month rate, and apply a discount: + +```javascript +monthRate(89, 0.42); +// => 9086 +``` + +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 + +Implement a function that takes a budget, a rate per hour and a discount, and calculates how many full days of work that covers. + +```javascript +daysInBudget(20000, 89, 0.2002); +// => 35 +``` + +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 new file mode 100644 index 0000000000..07f6172748 --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/introduction.md @@ -0,0 +1,11 @@ +# Introduction + +Many programming languages have specific numeric types to represent different types of numbers, but JavaScript only has two: + +- `number`: a numeric data type in the double-precision 64-bit floating point format (IEEE 754). + Examples are `-6`, `-2.4`, `0`, `0.1`, `1`, `3.14`, `16.984025`, `25`, `976`, `1024.0` and `500000`. +- `bigint`: a numeric data type that can represent _integers_ in the arbitrary precision format. + Examples are `-12n`, `0n`, `4n`, and `9007199254740991n`. + +If you require arbitrary precision or work with extremely large numbers, use the `bigint` type. +Otherwise, the `number` type is likely the better option. diff --git a/exercises/concept/numbers/.eslintrc b/exercises/concept/freelancer-rates/.eslintrc similarity index 100% rename from exercises/concept/numbers/.eslintrc rename to exercises/concept/freelancer-rates/.eslintrc diff --git a/exercises/concept/numbers/.gitignore b/exercises/concept/freelancer-rates/.gitignore similarity index 100% rename from exercises/concept/numbers/.gitignore rename to exercises/concept/freelancer-rates/.gitignore diff --git a/exercises/concept/numbers/.meta/config.json b/exercises/concept/freelancer-rates/.meta/config.json similarity index 76% rename from exercises/concept/numbers/.meta/config.json rename to exercises/concept/freelancer-rates/.meta/config.json index 4884b3d2ee..1ffaba2871 100644 --- a/exercises/concept/numbers/.meta/config.json +++ b/exercises/concept/freelancer-rates/.meta/config.json @@ -8,8 +8,8 @@ ], "contributors": [], "files": { - "solution": ["numbers.js"], - "test": ["numbers.spec.js"], + "solution": ["freelancer-rates.js"], + "test": ["freelancer-rates.spec.js"], "exemplar": [".meta/exemplar.js"] }, "forked_from": [] diff --git a/exercises/concept/freelancer-rates/.meta/design.md b/exercises/concept/freelancer-rates/.meta/design.md new file mode 100644 index 0000000000..6e02f5cc46 --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/design.md @@ -0,0 +1,21 @@ +# Design + +## Learning objectives + +- Know how to write floating point literals. +- 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`) + +## Out of scope + +- Parsing integers and floats from strings. +- Converting integers and floats to strings. + +## Concepts + +- `numbers` + +## Prerequisites + +- `basics` diff --git a/exercises/concept/freelancer-rates/.meta/exemplar.js b/exercises/concept/freelancer-rates/.meta/exemplar.js new file mode 100644 index 0000000000..da707c59cc --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/exemplar.js @@ -0,0 +1,67 @@ +// @ts-check +// +// ☝🏽 The line above enables type checking for this file. Various IDEs interpret +// the @ts-check directive. It will give you helpful autocompletion on the web +// and supported IDEs when implementing this exercise. You don't need to +// understand types, JSDoc, or TypeScript in order to complete this JavaScript +// exercise, and can completely ignore this comment block and directive. + +// 👋🏽 Hi again! +// +// A quick reminder about exercise stubs: +// +// 💡 You're allowed to completely clear any stub before you get started. Often +// we recommend using the stub, because they are already set-up correctly to +// work with the tests, which you can find in ./annalyns-infiltration.spec.js. +// +// 💡 You don't need to write JSDoc comment blocks yourself; it is not expected +// in idiomatic JavaScript, but some companies and style-guides do enforce them. +// +// Get those rates calculated! + +/** + * The day rate, given a rate per hour + * + * @param {number} ratePerHour + * @returns {number} the rate per day + */ +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); +} + +/** + * Applies a discount to the value + * + * @param {number} value + * @param {number} percentage for example 20% written as 0.2 + * @returns {number} the discounted value + */ +function applyDiscount(value, percentage) { + return (1 - percentage) * value; +} diff --git a/exercises/concept/numbers/.npmrc b/exercises/concept/freelancer-rates/.npmrc similarity index 100% rename from exercises/concept/numbers/.npmrc rename to exercises/concept/freelancer-rates/.npmrc diff --git a/exercises/concept/numbers/LICENSE b/exercises/concept/freelancer-rates/LICENSE similarity index 100% rename from exercises/concept/numbers/LICENSE rename to exercises/concept/freelancer-rates/LICENSE diff --git a/exercises/concept/numbers/babel.config.js b/exercises/concept/freelancer-rates/babel.config.js similarity index 100% rename from exercises/concept/numbers/babel.config.js rename to exercises/concept/freelancer-rates/babel.config.js diff --git a/exercises/concept/freelancer-rates/freelancer-rates.js b/exercises/concept/freelancer-rates/freelancer-rates.js new file mode 100644 index 0000000000..31e11d0217 --- /dev/null +++ b/exercises/concept/freelancer-rates/freelancer-rates.js @@ -0,0 +1,64 @@ +// @ts-check +// +// ☝🏽 The line above enables type checking for this file. Various IDEs interpret +// the @ts-check directive. It will give you helpful autocompletion on the web +// and supported IDEs when implementing this exercise. You don't need to +// understand types, JSDoc, or TypeScript in order to complete this JavaScript +// exercise, and can completely ignore this comment block and directive. + +// 👋🏽 Hi again! +// +// A quick reminder about exercise stubs: +// +// 💡 You're allowed to completely clear any stub before you get started. Often +// we recommend using the stub, because they are already set-up correctly to +// work with the tests, which you can find in ./freelancer-rates.spec.js. +// +// 💡 You don't need to write JSDoc comment blocks yourself; it is not expected +// in idiomatic JavaScript, but some companies and style-guides do enforce them. +// +// Get those rates calculated! + +/** + * The day rate, given a rate per hour + * + * @param {number} ratePerHour + * @returns {number} the rate per day + */ +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 + * @returns {number} the number of days + */ +export function daysInBudget(budget, ratePerHour, discount) { + throw new Error('Implement the daysInBudget function'); +} + +/** + * Applies a discount to the value + * + * @param {number} value + * @param {number} percentage for example 20% written as 0.2 + * @returns {number} the discounted value + */ +function applyDiscount(value, percentage) { + throw new Error('Implement the applyDiscount function'); +} diff --git a/exercises/concept/freelancer-rates/freelancer-rates.spec.js b/exercises/concept/freelancer-rates/freelancer-rates.spec.js new file mode 100644 index 0000000000..533fc3e178 --- /dev/null +++ b/exercises/concept/freelancer-rates/freelancer-rates.spec.js @@ -0,0 +1,131 @@ +// @ts-check + +import { dayRate, monthRate, daysInBudget } from './freelancer-rates'; + +const DIFFERENCE_PRECISION_IN_DIGITS = 6; + +describe('freelancer rates', () => { + describe('day rate', () => { + test('at 16/hour', () => { + const actual = dayRate(16); + expect(actual).toBe(128); + }); + + test('at 25/hour', () => { + const actual = dayRate(25); + expect(actual).toBe(200); + }); + + test('at 31.40/hour', () => { + const actual = dayRate(31.4); + expect(actual).toBeCloseTo(251.2, DIFFERENCE_PRECISION_IN_DIGITS); + }); + + test('at 89.89/hour', () => { + const actual = dayRate(89.89); + expect(actual).toBeCloseTo(719.12, DIFFERENCE_PRECISION_IN_DIGITS); + }); + + test('at 97.654321/hour', () => { + const actual = dayRate(97.654321); + expect(actual).toBeCloseTo(781.234568, DIFFERENCE_PRECISION_IN_DIGITS); + }); + }); + + 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 expected = 10; + + expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); + }); + + test('at 25/hour', () => { + const actual = daysInBudget(1280, 25, 0); + 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; + + 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; + + 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; + + 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; + + 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; + + expect(actual).toBeCloseTo(expected, DIFFERENCE_PRECISION_IN_DIGITS); + }); + }); + }); +}); diff --git a/exercises/concept/numbers/package.json b/exercises/concept/freelancer-rates/package.json similarity index 87% rename from exercises/concept/numbers/package.json rename to exercises/concept/freelancer-rates/package.json index 7d7ead2934..4c22a77a58 100644 --- a/exercises/concept/numbers/package.json +++ b/exercises/concept/freelancer-rates/package.json @@ -1,12 +1,12 @@ { - "name": "@exercism/javascript-concept-numbers", + "name": "@exercism/javascript-freelancer-rates", "description": "Exercism concept exercise on numbers", "author": "Derk-Jan Karrenbeld ", "private": true, "repository": { "type": "git", "url": "https://github.com/exercism/javascript", - "directory": "languages/javascript/exercises/concept/numbers" + "directory": "exercises/concept/freelancer-rates" }, "devDependencies": { "@babel/cli": "^7.13.10", diff --git a/exercises/concept/numbers/.docs/hints.md b/exercises/concept/numbers/.docs/hints.md deleted file mode 100644 index 1e14c91858..0000000000 --- a/exercises/concept/numbers/.docs/hints.md +++ /dev/null @@ -1,20 +0,0 @@ -# Hints - -## 1. Calculate the day rate given an hourly rate - -## 2. Calculate the month rate, given an hourly rate and a discount - -- There is a global built-in function to _parse_ a `string` to a fractional - number, ignoring non-numeric characters, such as the `%` (percent)-sign. -- `100% - discount` equals the percentage after the discount is applied. -- There is a built-in helper object called [`Math`][ref-math-object] with - functions to, for example, round-down (`floor`) or round-up (`ceil`). - -[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 - -- There is a function on the `number` (proto)type to return a "fixed" number of - decimals. -- First determine the dayRate, given the discount, then calculate the number of - days, and finally "fix" the number of decimals. diff --git a/exercises/concept/numbers/.docs/instructions.md b/exercises/concept/numbers/.docs/instructions.md deleted file mode 100644 index bcb64fc63d..0000000000 --- a/exercises/concept/numbers/.docs/instructions.md +++ /dev/null @@ -1,55 +0,0 @@ -# Instructions - -In this exercise you'll be writing code to help a freelancer communicate with a -project manager by providing a few utilities 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. - -The freelancer is offering to apply a discount if the project manager chooses -to let the freelancer bill per month, which can come in handy if there is a -certain budget the project manager has to work with. - -Discounts are modeled as fractional numbers followed by a `%` (percentage) -between `0.0%` (no discount) and `90.0%` (maximum discount). - -## Tasks - -## 1. Calculate the day rate given an hourly rate - -Implement a function to calculate the day rate given an hourly rate: - -```javascript -dayRate(89); -// => 712 -``` - -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 - -Implement a function to calculate the month rate, and apply a discount: - -```javascript -monthRate(89, '42%'); -// => 9086 -``` - -The discount is always passed as a `string`. The result _must_ be rounded up to -the nearest whole number. - -## 3. Calculate the number of workdays given a budget, rate and discount - -Implement a function that takes a budget, a rate per hour and a discount, and -calculates how many days of work that covers, to one decimal place. - -```javascript -daysInBudget(20000, 89, '20.02%'); -// => "35.1" -``` - -The discount is always passed as a `string`. The result is the number of days -to one decimal place, as a `string`. diff --git a/exercises/concept/numbers/.docs/introduction.md b/exercises/concept/numbers/.docs/introduction.md deleted file mode 100644 index fcab3fc3f9..0000000000 --- a/exercises/concept/numbers/.docs/introduction.md +++ /dev/null @@ -1,15 +0,0 @@ -# Introduction - -There are two different types of numbers in JavaScript: - -- `number`: a numeric data type in the double-precision 64-bit floating point - format (IEEE 754). Examples are `-6`, `-2.4`, `0`, `0.1`, `1`, `3.14`, - `16.984025`, `25`, `976`, `1024.0` and `500000`. -- `bigint`: a numeric data type that can represent _integers_ in the arbitrary - precision format. Examples are `-12n`, `0n`, `4n`, and `9007199254740991n`. - -In contrast, in many other programming languages different numeric types can exist, -for example: Integers, Floats, Doubles, or Bignums. - -If you require arbitrary precision or work with extremely large numbers, use the -`bigint` type. Otherwise, the `number` type is likely the better option. diff --git a/exercises/concept/numbers/.meta/exemplar.js b/exercises/concept/numbers/.meta/exemplar.js deleted file mode 100644 index ebb1c34534..0000000000 --- a/exercises/concept/numbers/.meta/exemplar.js +++ /dev/null @@ -1,48 +0,0 @@ -// @ts-check - -/** - * The day rate, given a rate per hour - * - * @param {number} ratePerHour - * @returns {number} the rate per day - */ -export function dayRate(ratePerHour) { - return ratePerHour * 8; -} - -/** - * Calculates the rate per month - * - * @param {number} ratePerHour - * @param {string} discount for example 20% - * @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 on one decimal - * - * @param {number} budget the total budget - * @param {number} ratePerHour the rate per hour - * @param {string} discount the discount to apply - * @returns {string} the number of days - */ -export function daysInBudget(budget, ratePerHour, discount) { - const discounted = applyDiscount(dayRate(ratePerHour), discount); - return (budget / discounted).toFixed(1); -} - -/** - * Applies a discount to the value - * - * @param {number} value - * @param {string} percentage a percentage such as '20%' - * @returns {number} the discounted value - */ -function applyDiscount(value, percentage) { - return ((100 - parseFloat(percentage)) / 100) * value; -} diff --git a/exercises/concept/numbers/numbers.js b/exercises/concept/numbers/numbers.js deleted file mode 100644 index 98297e07f9..0000000000 --- a/exercises/concept/numbers/numbers.js +++ /dev/null @@ -1,49 +0,0 @@ -// @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. - -/** - * The day rate, given a rate per hour - * - * @param {number} ratePerHour - * @returns {number} the rate per day - */ -export function dayRate(ratePerHour) { - throw new Error('Implement the dayRate function'); -} - -/** - * Calculates the rate per month - * - * @param {number} ratePerHour - * @param {string} discount for example 20% - * @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 on one decimal - * - * @param {number} budget the total budget - * @param {number} ratePerHour the rate per hour - * @param {string} discount the discount to apply - * @returns {string} the number of days - */ -export function daysInBudget(budget, ratePerHour, discount) { - throw new Error('Implement the daysInBudget function'); -} - -/** - * Applies a discount to the value - * - * @param {number} value - * @param {string} percentage a percentage such as '20%' - * @returns {number} the discounted value - */ -function applyDiscount(value, percentage) { - throw new Error('Implement the applyDiscount function'); -} diff --git a/exercises/concept/numbers/numbers.spec.js b/exercises/concept/numbers/numbers.spec.js deleted file mode 100644 index e37b1738db..0000000000 --- a/exercises/concept/numbers/numbers.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -// @ts-check - -import { dayRate, monthRate, daysInBudget } from './numbers'; - -describe('numbers', () => { - /** @type {Array<[number, number]>} */ - const DAY_RATE_FIXTURES = [ - [16, 128], - [25, 200], - [31.4, 251.2], - [89, 712], - [97.654321, 781.234568], - ]; - - DAY_RATE_FIXTURES.forEach(([ratePerHour, expected]) => { - test(`dayRate(${ratePerHour})`, () => { - const actual = dayRate(ratePerHour); - - expect(actual).toBeCloseTo(expected, 6); - }); - }); - - /** @type {Array<[number, string, number]>} */ - const MONTH_RATE_FIXTURES = [ - [16, '0.0%', 2816], - [25, '0%', 4400], - [25, '1.23%', 4346], - [25, '50%', 2200], - [31.4, '5%', 5251], - [89, '5%', 14881], - [97.654321, '5%', 16328], - ]; - - MONTH_RATE_FIXTURES.forEach(([ratePerHour, discount, expected]) => { - xtest(`monthRate(${ratePerHour}, '${discount}')`, () => { - const actual = monthRate(ratePerHour, discount); - - expect(actual).toEqual(expected); - }); - }); - - /** @type {Array<[number, number, string, string]>} */ - const BUDGET_CALCULATION_FIXTURES = [ - [1280, 16, '0%', '10.0'], - [1280, 25, '0%', '6.4'], - [1280, 25, '35.7%', '10.0'], - [10000, 25, '5%', '52.6'], - [10000, 31.4, '5%', '41.9'], - [10000, 89, '5%', '14.8'], - [10000, 97.654321, '5%', '13.5'], - ]; - - BUDGET_CALCULATION_FIXTURES.forEach( - ([budget, ratePerHour, discount, expected]) => { - xtest(`daysInBudget(${budget}, ${ratePerHour}, '${discount}')`, () => { - const actual = daysInBudget(budget, ratePerHour, discount); - - expect(actual).toEqual(expected); - }); - } - ); -});