I'm writing a function that will validate certain codes.
Codes are integers that should meet two criteria:
To avoid useless calculations, I only want the function to evaluate criterion 2 if the first one is met.
codes <- c(16, 19, 12, 8)
is_valid <- function(code) {
if (nchar(code) != 2) {
return(FALSE)
} else {
# This part is not relevant
val1 <- as.integer(substr(code, 1, 1))
val2 <- as.integer(substr(code, 2, 2))
sum <- val1 + val2
validator <- 10 - sum %% 3
return(validator == 0)
}
}
Issue: this works well if I pass a single element (e.g. is_valid(16)
, however if I try passing a vector is_valid(codes)
, I get this error:
Error in if (nchar(code) != 2) { : the condition has length > 1
I understand this is because if
is not vectorised. I tried replacing this with ifelse()
but it doesn't really work in this circumstance because the second criteria is a multi-line bit of code.
I have a feeling there's a simple programming solution to this.
The problem lies in the logical test if (nchar(code) != 2)
. In R, the logical test inside an if
statement must return a single logical TRUE or FALSE. In your case, it will return a logical vector:
codes <- c(16, 19, 12, 8)
nchar(codes) != 2
#> [1] FALSE FALSE FALSE TRUE
The if
statement will use the first element of this vector for flow control (while also emitting a warning), so your function will process the entire vector codes
via the else
statement.
My guess is that you want a vectorized output. In this case we can use ifelse
, or even simply check the two logical tests independently and return their results combined by a logical AND:
is_valid <- function(code) {
test1 <- nchar(code) == 2
val1 <- as.integer(substr(code, 1, 1))
val2 <- as.integer(substr(code, 2, 2))
sum <- val1 + val2
validator <- (10 - sum) %% 3
test2 <- validator == 0 & !is.na(validator)
return(test1 & test2)
}
is_valid(codes)
#> [1] TRUE TRUE FALSE FALSE
Created on 2023-02-07 with reprex v2.0.2