1

In R I am trying to create a function that would return true if the values are equal, or are both NA.

is.equal <- `%==%` <- function(x,y, vect=T) {
  res <- ifelse(is.na(x),is.na(y),ifelse(is.na(y),is.na(x),x==y))
  if(!vect){
    res <- all(res)
  }
  return(res)
}

c("a","b") %==% "a" #[1] TRUE TRUE
"a" %==% c("a", "b") #[1] TRUE
c("a","b") == "a" #[1]  TRUE FALSE

What did I do wrong, for this function to only consider the first element of the vector? I would expect the same result as ==


Note 1: seems that recycling is not applied as I'd have expected

> 1:5 %==% 3
[1] FALSE FALSE FALSE FALSE FALSE

However,

> 1:5 %==% rep(3,5)
[1] FALSE FALSE  TRUE FALSE FALSE

Note 2: this version of the function explicitly recycles, and works as intended, but I'd still like to know what was causing this behaviour in the first version:

is.equal <- `%==%` <- function(x,y, vect=T) {
  samelength <- length(x) == length(y)
  if(!samelength) {
    maxlength <- max(length(x), length(y))
    minlength <- min(length(x), length(y))
    if(minlength==1) {
      if(length(x) == maxlength) {
        y <- rep(y, maxlength)
      } else {
        x <- rep(x, maxlength)
      }
    } else stop('recycling of non-unit vectors disallowed cause bug')
    
  }
  
  res <- ifelse(is.na(x),is.na(y),ifelse(is.na(y),is.na(x),x==y))
  if(!vect){
    res <- all(res)
  }
  return(res)
}
> c("a","b") %==% "a"
[1]  TRUE FALSE
> "a" %==% c("a", "b")
[1]  TRUE FALSE
> c("a","b") == "a"
[1]  TRUE FALSE
> 1:5 %==% rep(3,5)
[1] FALSE FALSE  TRUE FALSE FALSE

Quite curious about this

gaut
  • 5,771
  • 1
  • 14
  • 45

1 Answers1

4

You’re right that it’s a recycling problem. Per the docs, "ifelse returns a value with the same shape as test" (my emphasis). In other words, while yes and no may be recycled to the length of test, test is not recycled to match a longer yes or no. So if test is length 1, it just takes the first value of yes or no.

The following implementation is simpler, doesn’t rely on ifelse(), and works as expected:

is.equal <- `%==%` <- function(x, y, vect = T) {
  res <- (is.na(x) & is.na(y)) | (!is.na(x) & !is.na(y) & x == y)
  if (!vect) res <- all(res)
  res
}

c("a","b") %==% "a"  # TRUE FALSE
"a" %==% c("a", "b") # TRUE FALSE
c("a", NA) %==% NA   # FALSE TRUE
NA %==% c("a", NA)   # FALSE TRUE
zephryl
  • 14,633
  • 3
  • 11
  • 30