rr-s4

Create S4 slots dynamically


I would like to create (or extend) slots of a defined S4 class dynamically, where slots names and types are stored in a variable.

A manual approach would be:

setClass("ClassA", representation(
   Slot1 = "character",
   Slot2 = "numeric",
   Slot3 = "character"
))

But I would like to retrieve slot names and types from a variable:

slotName <- list("slot1", "slot2", "slot3")
slotType <- list("character", "numeric", "character")
slots <- map2(slotName, slotType, ~ str_c(.x, "=", '"',  .y, '"'))

How can I create the class with NSE?

I tried with eval(parse_expr()) to quote the expression within the class definition:

y <- str_c(slotName[1], "=", '"', slotType[1], '"')

setClass("classA", representation(
    eval(parse_expr(y))
))

x <- new("classA") 

But here the structure of the object x is:

> str(x)
Formal class 'classA' [package ".GlobalEnv"] with 1 slot
  ..@ .Data: chr(0)`

with no slot1, unfortunately. Mapping vectors in the class definition did not work either.


Solution

  • When you need to set names of parameters, the better strategy is to build a named list and use do.call to run a function. It's best to avoid eval(); there are almost always better options. In this case you can do

    setClass("ClassA", 
      do.call("representation", setNames(slotType, slotName))
    )
    
    x <- new("ClassA") 
    str(x)
    # Formal class 'ClassA' [package ".GlobalEnv"] with 3 slots
    #   ..@ slot1: chr(0) 
    #   ..@ slot2: num(0) 
    #   ..@ slot3: chr(0)