Reputation: 13
I wrote a function in R to attach zeros such that any number between 1 and 100 comes out as 001 (1), 010 (10), and 100 (100) but I can't figure out why the if statements aren't qualifying like I would like them to.
id <- 1:11
Attach_zero <- function(id){
i<-1
for(i in id){
if(id[i] < 10){
id[i] <- paste("00",id[i], sep = "")
}
if((id[i] < 100)&&(id[i]>=10)){
id[i] <- paste("0",id[i], sep = "")
}
print(id[i])
}
}
The output is "001", "2", "3",... "010", "11"
I have no idea why the for loop is skipping middle integers.
Upvotes: 0
Views: 276
Reputation: 3397
If you try your function with id = c(1:3, 6:11)
:
Attach_zero(id)
##[1] "001"
##[1] "2"
##[1] "3"
##[1] "8"
##[1] "9"
##[1] "010"
##[1] "11"
##Error in if (id[i] < 10) { : missing value where TRUE/FALSE needed
What here happens is that the missing values are omitted because your i
values says so. The i<-1
does nothing as it is after that written with for (i in id)
which in turns gives i
for each loop the ith value of id
instead of an index. So if your id is id <- c(1:3, 6:11)
you will have unexpected results as showed.
Just correcting your function to include all the elements of the id:
Attach_zero <- function(id){
for(i in 1:length(id)){
if(id[i] < 10){
id[i] <- paste("00",id[i], sep = "")
}
if((id[i] < 100)&&(id[i]>=10)){
id[i] <- paste("0",id[i], sep = "")
}
print(id[i])
}
}
Attach_zero(id)
##[1] "001"
##[1] "2"
##[1] "3"
##[1] "6"
##[1] "7"
##[1] "8"
##[1] "9"
##[1] "010"
##[1] "11"
Note the number 7 in this output. And using sprintf as jbaums says, including it in a function:
Attach_zero <- function(id){
return(sprintf('%03d', id)) #You can change return for print if you want
}
Attach_zero(id)
## [1] "001" "002" "003" "006" "007" "008" "009" "010" "011"
Upvotes: 1
Reputation: 973
Try this:
id <- 1:11
Attach_zero <- function(id){
id1 <- id
i <- 1
for (i in seq_along(id)) {
if(id[i] < 10){
id1[i] <- paste("00", id[i], sep = "")
}
if(id[i] < 100 & id[i] >= 10){
id1[i] <- paste("0", id[i], sep = "")
}
}
print(id1)
}
Upvotes: 1
Reputation: 27388
The problem here is that you're assigning a character string (e.g. "001") to a numeric vector. When you do this, the entire id
vector is converted to character
(elements of a vector must be of one type).
So, after comparing 1
to 10
and assigning "001"
to id[1]
, the next element of id
is "2"
(i.e. character 2). When an inequality includes a character element (e.g. "2" < 10
), the numeric part is coerced to character, and alphabetic sorting rules apply. These rules mean that both "100"
and "10"
comes before "2"
, and so neither of your if
conditions are met. This is the case for all numbers except 10
, which according to alphabetic sorting is less than 100
, and so your second if
condition is met. When you get to 11
, neither condition is met once again, since the "word" "11"
comes after the word "100"
.
While there are a couple of ways to fix your function, this functionality exists in R (as mentioned in the comments), both with sprintf
and formatC
.
sprintf('%03d', 1:11)
formatC(1:11, flag=0, width=3)
# [1] "001" "002" "003" "004" "005" "006" "007" "008" "009" "010" "011"
For another vectorised approach, you could use nested ifelse
statements:
ifelse(id < 10, paste0('00', id), ifelse(id < 100, paste0('0', id), id))
Upvotes: 1