Validating Input
Part of the Logic & Flow section of Coddy's R journey. Lesson 55 of 64.
Input from users or files is text and can be anything. Check it before using it: suppressWarnings(as.numeric(s)) gives NA for text that is not a number, and is.na() tests for that:
read_age <- function(s) {
n <- suppressWarnings(as.numeric(s))
if (is.na(n)) return("not a number")
if (n < 0 || n > 130) return("out of range")
paste("age", n)
}
print(read_age("36"))
print(read_age("abc"))
print(read_age("200"))Output:
[1] "age 36"
[1] "not a number"
[1] "out of range"A validator can collect every problem instead of stopping at the first, which gives more useful feedback. Here it returns a vector of messages, empty when the input is fine:
check_password <- function(p) {
problems <- c()
if (nchar(p) < 8) problems <- c(problems, "too short")
if (!grepl("[0-9]", p)) problems <- c(problems, "needs a digit")
problems
}
print(check_password("abc"))
print(length(check_password("secret123")))Output:
[1] "too short" "needs a digit"
[1] 0Pick the style by who calls the function: return a message when the caller expects bad input and shows it to a user; raise an error with stop() when bad input means a bug that should not go unnoticed.
# the caller expects bad input and shows the message
read_age("abc") # "not a number"
# bad input is a mistake in the program
if (!is.numeric(x)) stop("x must be numeric")Validating with tryCatch() around the parsing step keeps the main code free of checks: each record is either converted or reported:
parse_record <- function(line) {
parts <- strsplit(line, ",")[[1]]
if (length(parts) != 2) stop("expected 2 fields")
qty <- suppressWarnings(as.integer(parts[2]))
if (is.na(qty)) stop("quantity is not a whole number")
list(item = parts[1], qty = qty)
}
for (line in c("tea,2", "jam", "oil,x")) {
r <- tryCatch(parse_record(line), error = function(e) conditionMessage(e))
cat(line, ":", if (is.list(r)) paste("ok", r$qty) else r, "\n")
}Output:
tea,2 : ok 2
jam : expected 2 fields
oil,x : quantity is not a whole number Challenge
EasyComplete check_signup(name, age, email). Collect every problem, in this order: name is empty (after removing spaces at the ends), age must be a number, age must be 13 or older (only when the age is a number), and email needs @. Return ok when there is no problem, otherwise the problems joined with ; .
The supplied code reads the name, the age and the email (one per line) and prints the returned value.
Try it yourself
check_signup <- function(name, age, email) {
# Write your code here
"ok"
}
# Supplied input/output code: keep it as it is
input <- suppressWarnings(readLines(file("stdin")))
cat(check_signup(input[1], input[2], input[3]), sep = "\n")
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Logic & Flow
1Strings In Depth
Substrings with substr()Formatting with sprintf()Splitting and JoiningSearching StringsReplacing TextRecap - Username Builder4Matrices
Creating MatricesIndexing MatricesRow and Column SummariesMatrix ArithmeticRecap - Seating Chart2Key-Value Lookups
Named Vector LookupsChecking KeysAdding and Removing KeysLooping Over NamesRecap - Stock Desk8Sorting and Grouping
Sorting VectorsSorting by a KeyGrouping with split()Group SummariesRecap - Leaderboard11Error Handling
Raising Errors with stop()Catching with tryCatch()Warnings and finallyValidating InputRecap - Safe Calculator3Sets and Counting
Unique ValuesSet OperationsMembership TestsCounting with table()Recap - Event Guests6Functions as Values
Anonymous FunctionsPassing FunctionsReturning FunctionsClosures with StateRecap - Discount Rules9Data Frames
Creating Data FramesColumns and RowsFiltering RowsAdding and SortingRecap - Sales Report12Project - Expense Tracker
Recording ExpensesTotal SpendingPractice on your own: Online R compiler