Doug Mitarotonda
Doug Mitarotonda

Reputation: 107

Dynamic list creation

I am vexed by the way R dynamically creates lists and I am hoping someone can help me understand what is going on and what to do to fix my code. My problem is that for assignments of a vector of length one, a named vector is assigned, but assignments of a vector of length greater than one, a list is assigned. My desired outcome is that a list is assigned no matter the length of the vector I am assigning. How do I achieve such a result?

For example,

types <- c("a", "b")
lst <- vector("list", length(types))
names(lst) <- types
str(lst)
List of 2
 $ a: NULL
 $ b: NULL
lst$a[["foo"]] <- "hi"
lst$b[["foo"]] <- c("hi", "SO")
str(lst)
List of 2
 $ a: Named chr "hi"
  ..- attr(*, "names")= chr "foo"
 $ b:List of 1
  ..$ foo: chr [1:2] "hi" "SO"
str(lst$a)
 Named chr "hi"
 - attr(*, "names")= chr "foo"
str(lst$b)
List of 1
 $ foo: chr [1:2] "hi" "SO"

What I want to have as the outcome is a data structure that looks like this.

List of 2
 $ a:List of 1
  ..$ foo: chr [1] "hi"
 $ b:List of 1
  ..$ foo: chr [1:2] "hi" "SO"

Upvotes: 3

Views: 10042

Answers (2)

flodel
flodel

Reputation: 89097

While I also find it surprising, it is documented in ?[[:

Recursive (list-like) objects:

 [...]

 When ‘$<-’ is applied to a ‘NULL’ ‘x’, it first coerces ‘x’ to
 ‘list()’.  This is what also happens with ‘[[<-’ if the
 replacement value ‘value’ is of length greater than one: if
 ‘value’ has length 1 or 0, ‘x’ is first coerced to a zero-length
 vector of the type of ‘value’.

To override that behavior, you could specifically create empty lists before dynamically assigning to them:

lst$a <- list()
lst$b <- list()

or like Josh suggested below, replace your lst <- vector("list", length(types)) with lst <- replicate(length(types), list()).

Now that ‘x’ (lst$a or lst$b) is not ‘NULL’ but an empty list, your code should work as you expected:

lst$a[["foo"]] <- "hi"
lst$b[["foo"]] <- c("hi", "SO")
str(lst)
# List of 2
#  $ a:List of 1
#   ..$ foo: chr "hi"
#  $ b:List of 1
#   ..$ foo: chr [1:2] "hi" "SO"

Upvotes: 3

Dirk is no longer here
Dirk is no longer here

Reputation: 368629

I think you just need to create the types you want and assign them:

R> qq <- list( a=list(foo="Hi"),  b=list(foo=c("Hi", "SO")))
R> qq
$a
$a$foo
[1] "Hi"


$b
$b$foo
[1] "Hi" "SO"


R> 

where all your requirements are met:

R> class(qq)
[1] "list"
R> names(qq)
[1] "a" "b"
R> sapply(qq, names)
    a     b 
"foo" "foo" 
R> 

Upvotes: 2

Related Questions