Skip to content

[V3] Freelancer Rates: fix everything around this exercise #1021

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions concepts/comparison/about.md
Original file line number Diff line number Diff line change
@@ -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`.
1 change: 1 addition & 0 deletions concepts/comparison/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Introduction
1 change: 1 addition & 0 deletions concepts/comparison/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
96 changes: 95 additions & 1 deletion concepts/numbers/about.md
Original file line number Diff line number Diff line change
@@ -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

<!--
These are widgets. See: https://github.com/exercism/docs/blob/main/anatomy/tracks/widgets.md
Normally these would be put in a list, but it renders better when it's next to each other.
-->

[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
18 changes: 7 additions & 11 deletions concepts/numbers/introduction.md
Original file line number Diff line number Diff line change
@@ -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.
19 changes: 18 additions & 1 deletion concepts/numbers/links.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
5 changes: 3 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@
"status": "active"
},
{
"slug": "numbers",
"slug": "freelancer-rates",
"name": "Freelancer Rates",
"uuid": "0aff2fa7-55ea-47e9-af4a-78927d916baf",
"concepts": ["numbers"],
"prerequisites": ["basics"],
Expand Down Expand Up @@ -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",
Expand Down
18 changes: 18 additions & 0 deletions exercises/concept/freelancer-rates/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -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.
47 changes: 47 additions & 0 deletions exercises/concept/freelancer-rates/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 11 additions & 0 deletions exercises/concept/freelancer-rates/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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": []
Expand Down
21 changes: 21 additions & 0 deletions exercises/concept/freelancer-rates/.meta/design.md
Original file line number Diff line number Diff line change
@@ -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`
67 changes: 67 additions & 0 deletions exercises/concept/freelancer-rates/.meta/exemplar.js
Original file line number Diff line number Diff line change
@@ -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;
}
Loading