166

Let's say we have a statement that produces integer(0), e.g.

 a <- which(1:3 == 5)

What is the safest way of catching this?

mbq
  • 18,510
  • 6
  • 49
  • 72
Roman Luštrik
  • 69,533
  • 24
  • 154
  • 197
  • I don't like the idea of treating it as an error -- in fact R's policy of not collapsing certain empty objects helps to avoid many error-recover flows, and thus leads to much cleaner code. – mbq Jun 23 '11 at 10:56
  • 22
    Don't use which. – hadley Jun 23 '11 at 13:54
  • 3
    You can test with `any`. It will return FALSE for either `which(1:3==5)` or for `1:3==5` . – IRTFM Jun 18 '14 at 00:12
  • 1
    @BondedDust I was trying to find `integer(0)`, which I produced using `which` as an example. – Roman Luštrik Jun 18 '14 at 10:20
  • 11
    I know this is old, but could you, hadley, please outline why not to use `which`? This would be very helpful for me to avoid bad code. – Cactus Jan 09 '18 at 22:50
  • Maybe because it can introduce bugs and often not necessary see discussion at the bottom of [this page](http://adv-r.had.co.nz/Subsetting.html). – s_baldur May 02 '18 at 13:49

8 Answers8

186

That is R's way of printing a zero length vector (an integer one), so you could test for a being of length 0:

R> length(a)
[1] 0

It might be worth rethinking the strategy you are using to identify which elements you want, but without further specific details it is difficult to suggest an alternative strategy.

Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
  • It may be worth to point out to the future reader that `length(NULL) == 0` is also `TRUE`. [user E Nord's answer](https://stackoverflow.com/a/65706705/7941188) would be an option to test for `integer(0)` only – tjebo Jan 18 '21 at 09:26
25

If it's specifically zero length integers, then you want something like

is.integer0 <- function(x)
{
  is.integer(x) && length(x) == 0L
}

Check it with:

is.integer0(integer(0)) #TRUE
is.integer0(0L)         #FALSE
is.integer0(numeric(0)) #FALSE

You can also use assertive for this.

library(assertive)
x <- integer(0)
assert_is_integer(x)
assert_is_empty(x)
x <- 0L
assert_is_integer(x)
assert_is_empty(x)
## Error: is_empty : x has length 1, not 0.
x <- numeric(0)
assert_is_integer(x)
assert_is_empty(x)
## Error: is_integer : x is not of class 'integer'; it has class 'numeric'.
Richie Cotton
  • 118,240
  • 47
  • 247
  • 360
  • 6
    You could just use `!length(x)` rather than `length(x)==0` – James Jun 23 '11 at 10:42
  • 4
    @James. True, but I don't think there's much of a performance issue either way, and `length(x) == 0L` reads more clearly to me. – Richie Cotton Jun 24 '11 at 10:04
  • @RichieCotton. What's up with 0L as opposed to 0? I've tried googling it, but I'm not finding anything relevant. Sorry about the necromancy. – eenblam Aug 21 '13 at 22:36
  • 2
    @Ben: Adding an `L` suffix to a number makes R store it as an integer rather than a floating point value. See, e.g., http://cran.r-project.org/doc/manuals/R-lang.html#Constants – Richie Cotton Sep 02 '13 at 11:45
  • Thanks! It's saved my time. – Andrii Apr 16 '18 at 16:44
13

Maybe off-topic, but R features two nice, fast and empty-aware functions for reducing logical vectors -- any and all:

if(any(x=='dolphin')) stop("Told you, no mammals!")
mbq
  • 18,510
  • 6
  • 49
  • 72
  • 1
    Yeah, would be great if there was something like `is.empty`, cuz some functions return `integer(0)` instead of `NA` or `NULL`. But for now your way is the most straightforward, and works vector-wise which is a big advantage over `length(a)`. – Ufos Feb 26 '18 at 09:54
8

Inspired by Andrie's answer, you could use identical and avoid any attribute problems by using the fact that it is the empty set of that class of object and combine it with an element of that class:

attr(a, "foo") <- "bar"

identical(1L, c(a, 1L))
#> [1] TRUE

Or more generally:

is.empty <- function(x, mode = NULL){
    if (is.null(mode)) mode <- class(x)
    identical(vector(mode, 1), c(x, vector(class(x), 1)))
}

b <- numeric(0)

is.empty(a)
#> [1] TRUE
is.empty(a,"numeric")
#> [1] FALSE
is.empty(b)
#> [1] TRUE
is.empty(b,"integer")
#> [1] FALSE
tjebo
  • 21,977
  • 7
  • 58
  • 94
James
  • 65,548
  • 14
  • 155
  • 193
7
if ( length(a <- which(1:3 == 5) ) ) print(a)  else print("nothing returned for 'a'") 
#[1] "nothing returned for 'a'"

On second thought I think any is more beautiful than length(.):

 if ( any(a <- which(1:3 == 5) ) ) print(a)  else print("nothing returned for 'a'") 
 if ( any(a <- 1:3 == 5 ) ) print(a)  else print("nothing returned for 'a'") 
IRTFM
  • 258,963
  • 21
  • 364
  • 487
3

You can easily catch integer(0) with function identical(x,y)

x = integer(0)
identical(x, integer(0))
[1] TRUE

foo = function(x){identical(x, integer(0))}
foo(x)
[1] TRUE

foo(0)
[1] FALSE
tjebo
  • 21,977
  • 7
  • 58
  • 94
E Nord
  • 31
  • 2
2

another option is rlang::is_empty (useful if you're working in the tidyverse)

The rlang namespace does not seem to be attached when attaching the tidyverse via library(tidyverse) - in this case you use purrr::is_empty, which is just imported from the rlang package.

By the way, rlang::is_empty uses user Gavin's approach.

rlang::is_empty(which(1:3 == 5))
#> [1] TRUE
tjebo
  • 21,977
  • 7
  • 58
  • 94
0

isEmpty() is included in the S4Vectors base package. No need to load any other packages.

a <- which(1:3 == 5)
isEmpty(a)
# [1] TRUE