Search code examples
rgenericssubsetenvironmentr-s4

S4 method subsetting generic replace environment


I am getting lost in the documentation and really losing track of what to do. I think that the solution is to use environments but I cannot figure out how even if I feel it is not that complicated.

Here is a simple example using two classes:

Person <- setClass(        
    Class = "Person",
    slots = c(name = "character",
            id = "numeric",
            age = "numeric"));

 PersonList <- setClass(   
    Class = "PersonList",
    slots = c(datasetList = "list"));

Here is the objects creation:

mary <- new("Person", name = "Mary", id = 1, age = 35);
peter <- new("Person", name = "Peter", id = 2, age = 39);
john <- new("Person", name = "John", id = 3, age = 25);
employees <- new("PersonList", datasetList = list(mary, peter, john));

I am then defining two methods to set the age and the id, and a method for sub-setting PersonList:

setGeneric(name = "setAge<-", def = function(theObject, value){standardGeneric("setAge<-");});
setGeneric(name = "setid<-", def = function(theObject, value){standardGeneric("setid<-");});

setReplaceMethod(

    f = "setAge",

    signature = "Person",

    definition = function(theObject, value){
        theObject@age <- value;
        return(theObject);
    });

setReplaceMethod(

    f = "setid",

    signature = "Person",

    definition = function(theObject, value){
        theObject@id <- value;
        return(theObject);
    });

setMethod(

    f = "[[",

    signature = c("PersonList", "ANY", "ANY"),

    definition = function(x, i, j, ...){

        return(x@datasetList[[i]]);
    });

Now here is the problem:

> setAge(employees[[1]]) <-56
Error in `[[<-`(`*tmp*`, 1, value = new("Person",
            name = "Mary", id = 1,  : 
                    [[<- defined for objects of type "S4" only for subclasses of environment

If I understand correctly I have to use environments to get something like:

setMethod(

    f = "[[<-",

    signature = c("PersonList", "ANY", "ANY"),

    definition = function(x, i, j, ...){

        if environment is setid return(setId(x[[i]]))
        if environment is setAge return(setAge(x[[i]]))
    });

Going through the doc this is getting really complicated. Can anyone give me a hint on how to do this?

Thanks a lot.


Solution

  • All you need is to define [[<- for your PersonList class:

    setMethod(
        f = "[[<-",
        signature = c("PersonList"),
        definition=function(x,i,j,value) {
            x@datasetList[[i]] <- value
            return(x)
        })
    

    Then your code should work. You could add some code to check integrity, though. Like inherits(value, "Person") and missing(j).