Reputation: 38682
I have different possible input values, and I want to split them in two sets, one that contains single / atomar items, and one that contains multiple items or list-like structures that are empty.
For example, take these values:
123
"foo"
c()
c(c())
list()
list(list())
c(1, 2, 3)
c("a", "b", "c")
c(c("a", 1), "b", "c")
list("foo", "bar", c("baz", "blah"))
The first two should go into category A, the rest should go into category B.
I have tried various combinations of is.recursive
or is.atomic
, but I never get a correct split, as for example both 123
and c(1)
are considered as atomic, numeric, and a vector of length 1.
I created an overview table of the different logical tests, but I can't seem to find something that distinguishes the first two lines from the others:
Am I missing something obvious here, like some property that'd help me distinguish those classes better?
(except categories)
types <- list(123, "foo", c(), c(c()), list(), list(list()), c(1, 2, 3), c("a", "b", "c"), c(c("a", 1), "b", "c"), list("foo", "bar", c("baz", "blah")))
data.frame(types = paste(types),
is.recursive = sapply(types, is.recursive),
is.atomic = sapply(types, is.atomic),
is.character = sapply(types, is.character),
is.numeric = sapply(types, is.numeric),
is.vector = sapply(types, is.vector),
is.list = sapply(types, is.list),
length = sapply(types, length))
Upvotes: 2
Views: 65
Reputation: 38682
Based on the hint from @loki I found a solution that works for me:
if (is.null(x) || is.list(x) || (is.vector(x) && (length(x) > 1))) {
print("Category B")
} else {
print("Category A")
}
This now produces:
0 # => "Option A"
123 # => "Option A"
"foo" # => "Option A"
c(1) # => "Option A"
c() # => "Option B"
c(c()) # => "Option B"
list() # => "Option B"
list(list()) # => "Option B"
c(1, 2, 3) # => "Option B"
c("a", "b", "c") # => "Option B"
c(c("a", 1), "b", "c") # => "Option B"
list("foo", "bar", c("baz", "blah")) # => "Option B"
It is worth noting that c(1) == 1
, and reading the introduction to data structures helps, too.
Upvotes: 0
Reputation: 10350
You can use a combination of length()
, is.vector()
and is.null()
types <- list(123, "foo", c(), c(c()), list(), list(list()), c(1), c(1, 2, 3), c("a", "b", "c"), c(c("a", 1), "b", "c"), list("foo", "bar", c("baz", "blah")))
data.frame(types = paste(types),
is.recursive = sapply(types, is.recursive),
is.atomic = sapply(types, is.atomic),
is.character = sapply(types, is.character),
is.numeric = sapply(types, is.numeric),
is.vector = sapply(types, is.vector),
is.list = sapply(types, is.list),
length = sapply(types, length),
is.null = sapply(types, is.null),
typeof = sapply(types, typeof),
class = sapply(types, class),
# and now let's get to the mystery using 4 of these values:
category = sapply(types, function(x){
ifelse(is.null(x) || is.list(x) || (is.vector(x) && length(x) > 1), "B", "A")
}))
# types ... category
#1 123 ... A
#2 foo ... A
#3 c() ... B
#4 c(c()) ... B
#5 list() ... B
#6 list(list()) ... B
#7 c(1) ... A
#8 c(1, 2, 3) ... B
#9 c("a", "b", "c") ... B
#10 c("a", "1", "b", "c") ... B
#11 list("foo", "bar", c("baz", "blah")) ... B
Furthermore, you should have a look at rapportools::is.empty()
. However, it fails for nested lists (list(list())
).
Upvotes: 1