Search code examples
rinheritancer6

R6 classes, get all fields as named list


I would like to retrieve the values of all my attributes of R6Class objects as a list. Preferably the values would come with the attribute names. Ideally this could be written generally so that it works for inherited classes as well. Unfortunately the R6 documentation is not so comprehensive and I couldn't find a comparable question.

The following basic example should demonstrate what I currently have and what the target solution should look like.

Person <- R6::R6Class("Person", public = list(
  age = NULL,
  gender = NULL,
  
  initialize = function(age, gender = "M") {
    self$age <- age
    self$gender <- gender
  },
  
  list_attributes = function(){
    return(list(self$age, self$gender))
  }
))

p <- Person$new(age=42, gender="W")
p$list_attributes()

# Output
> [[1]]
> [1] 42

> [[2]]
> [1] "W"

The list_attributes partially does what I want but I think there should be a better way to get all public attributes than explicitly naming all of them. I found that with str(p) I get some information about my object but not in the form I want it.

> str(p)
Classes 'Person', 'R6' <Person>
  Public:
    age: 42
    clone: function (deep = FALSE) 
    gender: W
    initialize: function (age, gender = "M") 
    list_attributes: function () 

My desired outut would like:

list(age=42, gender="M")
$age
[1] 42

$gender
[1] "M"

Does anyone have an idea how to achieve this?


Solution

  • Thanks for your question and answer, I was struggling with the same issue.

    This issue links to a question I asked recently on changing the class of self within an object, here.

    As it was explained to me, it is possible to access the object class within the object using class(self), so no need to use a private "class" field, and so your problem can be solved using your object structure as:

    Person <- R6::R6Class("Person",                     
                          
                          public = list(
                            age = NULL,
                            gender = NULL,
                            
                            initialize = function(age, gender = "M") {
                              self$age <- age
                              self$gender <- gender
                            },
                            
                            public_fields = function(){
                              return(names(get(class(self))$public_fields))
                            },
                            
                            list_attributes = function(){
                              
                              values <- purrr::map(self$public_fields(), ~.subset2(self, .x))
                              names(values) <- self$public_fields()
                              
                              return(values)
                            }
                          ))
    

    Which gives:

    > p <- Person$new(age=42, gender="W")
    > p$public_fields()
    [1] "age"    "gender"
    > p$list_attributes()
    $age
    [1] 42
    
    $gender
    [1] "W"
    

    Or, by putting it all in one active field, and using set_names():

    Person <- R6::R6Class("Person",                     
                          
                          public = list(
                            age = NULL,
                            gender = NULL,
                            
                            initialize = function(age, gender = "M") {
                              self$age <- age
                              self$gender <- gender
                            }
                            ),
                          active = list(
                            
                            my_fields = function(){
                              purrr::map(names(get(class(self))$public_fields), ~.subset2(self, .x)) %>% 
                                set_names(names(get(class(self))$public_fields))
                            }
                          ))
    

    Which gives:

    > p <- Person$new(age=42, gender="W")
    > p$my_fields
    $age
    [1] 42
    
    $gender
    [1] "W"