diff --git a/config.json b/config.json index 01ec79ac..bba10daf 100644 --- a/config.json +++ b/config.json @@ -65,6 +65,19 @@ "booleans" ], "status": "wip" + }, + { + "slug": "name-badges", + "name": "Name Badges", + "uuid": "31f8973a-5035-4b54-9670-e589fa59361b", + "concepts": [ + "nothingness" + ], + "prerequisites": [ + "vector-filtering", + "strings" + ], + "status": "wip" } ], "practice": [ diff --git a/exercises/concept/name-badges/.docs/hints.md b/exercises/concept/name-badges/.docs/hints.md new file mode 100644 index 00000000..b5296c36 --- /dev/null +++ b/exercises/concept/name-badges/.docs/hints.md @@ -0,0 +1 @@ +# Hints diff --git a/exercises/concept/name-badges/.docs/instructions.md b/exercises/concept/name-badges/.docs/instructions.md new file mode 100644 index 00000000..dab5ab12 --- /dev/null +++ b/exercises/concept/name-badges/.docs/instructions.md @@ -0,0 +1,54 @@ +# Instructions + +In this exercise you'll be writing code to print name badges for factory employees. Employees have an ID, name, and department name. Employee badge labels are formatted as follows: `"[id] - name - DEPARTMENT"`. + +## 1. Print a badge for an employee + +Implement the `print_name_badge` function. It should take an ID, name, and a department. It should return the badge label, with the department name in uppercase. + +```R +print_name_badge(67, "Katherine Williams", "Strategic Communication") +# => "[67] - Katherine Williams - STRATEGIC COMMUNICATION" +``` + +## 2. Print a badge for a new employee + +Due to a quirk in the computer system, new employees occasionally don't yet have an ID when they start working at the factory. As badges are required, they will receive a temporary badge without the ID prefix. + +Extend the `print_name_badge` function. When the id is missing, it should print a badge without it. + +```R +print_name_badge(NA, "Robert Johnson", "Procurement") +# => "Robert Johnson - PROCUREMENT" +``` + +## 3. Print a badge for the owner + +Even the factory's owner has to wear a badge at all times. However, an owner does not have a department and never will: he is above all the departments. In this case, the label should print `"OWNER"` instead of the department name. + +Extend the `print_name_badge` function. When the department is `NULL`, assume the badge belongs to the company owner. + +```R +print_name_badge(204, "Rachel Miller", NULL) +# => "[204] - Rachel Miller - OWNER" +``` + +Note that it is possible for the owner to also be a new employee. + +```R +print_name_badge(NA, "Rachel Miller", NULL) +# => "Rachel Miller - OWNER" +``` + +## 4. Calculate the total salary of emplyees with no ID + +As a rough metric of how well the IDs are being issued, you want to see the combined salary of employees with no ID. A high value means lots are waiting, or the problem is affecting senior people: both bad. + +Implement the `salaries_no_id` function that takes a vector of IDs and a corresponding vector of salaries, and returns the sum of salaries for people with no ID yet. Both vectors are the same length. + +```R +ids <- c(204, NA, 210, 352, NA, 263) +salaries <- c(23, 21, 47, 35, 17, 101) * 1000 +salaries_no_id(ids, salaries) +# => 38,000 +``` diff --git a/exercises/concept/name-badges/.docs/introduction.md b/exercises/concept/name-badges/.docs/introduction.md new file mode 100644 index 00000000..222abf7b --- /dev/null +++ b/exercises/concept/name-badges/.docs/introduction.md @@ -0,0 +1,39 @@ +# Introduction + +Many languages have a way such as `null` or `none` to indicate a non-existent value. +Because R is designed to handle large volumes of (often messy) data, it has multiple forms of nothingness. + +The overall aim is to flag missing or suspect values as they are encountered, then continue without raising an exception. + +## NULL + +If a value really doesn't exist, it is repesented by `NULL`. This is probably closest to what C or Python might do. + +```R +> v <- c() # zero-length vector +> v +NULL +> is.null(v) +[1] TRUE +``` + +In many contexts, `NULL` values are simply ignored: + +```R +> c(2, 3, NULL, 5) +[1] 2 3 5 +``` + +## NA + +For situations where a value exists but we don't know what it is, `NA` is used. For example, when counting vehicles traveling on a road, human observers might go off sick or automatic sensors break down, but the traffic continues to flow. + +```R +> v <- c(1, 2, NA, 4, 5) +> v +[1] 1 2 NA 4 5 +> is.na(v) # test for data gaps +[1] FALSE FALSE TRUE FALSE FALSE +``` + +Thus `NA` is a placeholder, warning humans that they need to make a decision about how to handle this. diff --git a/exercises/concept/name-badges/.meta/config.json b/exercises/concept/name-badges/.meta/config.json new file mode 100644 index 00000000..79bf3ffd --- /dev/null +++ b/exercises/concept/name-badges/.meta/config.json @@ -0,0 +1,11 @@ +{ + "authors": ["colinleach"], + "contributors": [], + "files": { + "solution": ["name-badges.R"], + "test": ["test_name-badges.R"], + "exemplar": [".meta/exemplar.R"] + }, + "forked_from": ["elixir/name-badge"], + "blurb": "Handle missing values in employee name badges" +} diff --git a/exercises/concept/name-badges/.meta/design.md b/exercises/concept/name-badges/.meta/design.md new file mode 100644 index 00000000..4e704e09 --- /dev/null +++ b/exercises/concept/name-badges/.meta/design.md @@ -0,0 +1,26 @@ +# Design + +## Goal + +The goal of this exercise is to teach the student about the various forms of nothingness in R. + +## Learning objectives + +- Understand `NULL` as the absence of a value. +- Understand `NA` as a placeholder for a missing value. +- Understand the basics of how to test for and respond to these values. +- Understand that R tries to flag these values in-place *without* throwing an exception: a data-science approach rather than a computer-science approach. + +## Out of scope + +- `NaN` and `Inf` as flagging invalid numbers, though this is mentioned in the concept's `about.md`. Omitting these is purely pragmatic, with no obvious way to include them in the exercise. +- The greater importance of this concept in more complex structures such as `dataframes`. At this stage, the discussion is limited to vectors. + +## Concepts + +- `errors` + +## Prerequisites + +- `vector-filtering` +- `strings` diff --git a/exercises/concept/name-badges/.meta/exemplar.R b/exercises/concept/name-badges/.meta/exemplar.R new file mode 100644 index 00000000..7d682609 --- /dev/null +++ b/exercises/concept/name-badges/.meta/exemplar.R @@ -0,0 +1,11 @@ +print_name_badge <- function(id, name, department) { + dept <- ifelse(is.null(department), "OWNER", toupper(department)) + if (is.na(id)) { + return(sprintf("%s - %s", name, dept)) + } + sprintf("[%d] - %s - %s", id, name, dept) +} + +salaries_no_id <- function(ids, salaries) { + sum(salaries[is.na(ids)]) +} diff --git a/exercises/concept/name-badges/name-badges.R b/exercises/concept/name-badges/name-badges.R new file mode 100644 index 00000000..7e1ca35d --- /dev/null +++ b/exercises/concept/name-badges/name-badges.R @@ -0,0 +1,5 @@ +print_name_badge <- function(id, name, department) { +} + +salaries_no_id <- function(ids, salaries) { +} diff --git a/exercises/concept/name-badges/test_name-badges.R b/exercises/concept/name-badges/test_name-badges.R new file mode 100644 index 00000000..17a8323b --- /dev/null +++ b/exercises/concept/name-badges/test_name-badges.R @@ -0,0 +1,58 @@ +source("./name-badges.R") +library(testthat) + +# 1) print_name_badge + +test_that("prints the employee badge with full data", { + id <- 455 + name <- "Mary M. Brown" + department <- "MARKETING" + expected <- "[455] - Mary M. Brown - MARKETING" + expect_equal(print_name_badge(id, name, department), expected) +}) + +test_that("uppercases the department", { + id <- 89 + name <- "Jack McGregor" + department <- "Procurement" + expected <- "[89] - Jack McGregor - PROCUREMENT" + expect_equal(print_name_badge(id, name, department), expected) +}) + +test_that("prints the employee badge without id", { + id <- NA + name <- "Barbara White" + department <- "SECURITY" + expected <- "Barbara White - SECURITY" + expect_equal(print_name_badge(id, name, department), expected) +}) + +test_that("prints the owner badge", { + id <- 1 + name <- "Anna Johnson" + department <- NULL + expected <- "[1] - Anna Johnson - OWNER" + expect_equal(print_name_badge(id, name, department), expected) +}) + +test_that("prints the owner badge without id", { + id <- NA + name <- "Stephen Dann" + department <- NULL + expected <- "Stephen Dann - OWNER" + expect_equal(print_name_badge(id, name, department), expected) +}) + +# salaries_no_id + +test_that("sums salaries for employees without ID", { + ids <- c(201, 217, NA, 352, 102, NA, 263) + salaries <- c(19, 23, 42, 29, 65, 122, 54) + expect_equal(salaries_no_id(ids, salaries), 164) +}) + +test_that("sums salaries but no employees without ID", { + ids <- c(201, 217, 47, 352, 102, 163, 263) + salaries <- c(25, 27, 44, 26, 63, 122, 34) + expect_equal(salaries_no_id(ids, salaries), 0) +})