How to extend as.list in a canonical way to S4 objects
I am having trouble converting my S4 object to a list. Take, for example, the following nested S4 classes:
setClass("nssItem",
representation(value = "numeric", text = "character", prefix = "character", type = "character"),
prototype(value = as.numeric(NA), text = as.character(NA), prefix = as.character(NA), type = as.character(NA))
)
setClass("geckoNss", representation(absolute = "character", item = "nssItem"))
Class object geckoNss
contains class objects nssItem
. Conceptually, this is similar to a list-like structure that allows nesting.
Nevertheless,
> temp <- new("nssItem")
> as.list(temp)
Error in as.list.default(temp) :
no method for coercing this S4 class to a vector
I understand this error, meaning I have not actually defined what it means as.list
or how it relates to the class nssItem
. However, this seems like a very natural operation. How do I extend the definition as.list
to all new classes that I define?
source to share
This is the second, more general solution. It uses a superclass from which you get all custom classes. Descriptions are in the comments #
.
#this is an "empty" superclass that characterises all user-defined classes
setClass("user_defined_class")
#we create an as.list method for this new superclass (this time an S4 method)
setMethod("as.list",signature(x="user_defined_class"),function(x) {
mapply(function(y) {
#apply as.list if the slot is again an user-defined object
#therefore, as.list gets applied recursively
if (inherits(slot(x,y),"user_defined_class")) {
as.list(slot(x,y))
} else {
#otherwise just return the slot
slot(x,y)
}
},
slotNames(class(x)),
SIMPLIFY=FALSE)
})
setClass("nssItem",
representation(value = "numeric",
text = "character",
prefix = "character",
type = "character"),
prototype(value = as.numeric(NA),
text = as.character(NA),
prefix = as.character(NA),
type = as.character(NA)),
#note the contains argument that flags the nssItem class as user-defined
contains="user_defined_class")
setClass("geckoNss",
representation(absolute = "character", item = "nssItem"),
#the same for the geckoNss class
contains="user_defined_class")
Now create one object for each class
temp <- new("nssItem")
tempGecko<-new("geckoNss")
Forcing temp
to display
as.list(temp)
#$value
#[1] NA
#
#$text
#[1] NA
#
#$prefix
#[1] NA
#
#$type
#[1] NA
And the object tempGecko
as.list(tempGecko)
#$absolute
#character(0)
#
#$item
#$item$value
#[1] NA
#
#$item$text
#[1] NA
#
#$item$prefix
#[1] NA
#
#$item$type
#[1] NA
source to share
I'm not sure if I understood your "nesting" argument correctly, but there is code here on how to extend as.list
to your classes S4
. As Alex pointed out in the comments, these are actually S3
methods that are used on objects S4
. This works too. You can find a good summary on this topic here Combining S4 and S3 methods in one function
as.list.nssItem=function(from) mapply(function(x) slot(from,x),
slotNames("nssItem"),
SIMPLIFY=FALSE)
Now try as.list
on an nssItem
object temp
(as defined in your post)
as.list(temp)
#$value
#[1] NA
#
#$text
#[1] NA
#
#$prefix
#[1] NA
#
#$type
#[1] NA
Edit: I think now I understand what you mean by nesting. After executing the above code, define a new objectgeckoNss
tempGecko<-new("geckoNss")
Expand as.list
to classgeckoNss
as.list.geckoNss=function(from) mapply(function(x) {
if (x=="item") as.list(slot(from,x)) else slot(from,x)
},
slotNames("geckoNss"),
SIMPLIFY=FALSE)
Now apply as.list
to your objectgeckoNss
tempGecko
as.list(tempGecko)
#$absolute
#character(0)
#
#$item
#$item$value
#[1] NA
#
#$item$text
#[1] NA
#
#$item$prefix
#[1] NA
#
#$item$type
#[1] NA
As per Alex's comment below, here's a more general way to extend as.list
.
#save old function definition (just in case...)
as.list.default.save=as.list.default
Define a new default method
as.list.default=function(x) {
if (class(x)=='list') {
x
} else if (class(x)%in%c('nssItem','geckoNss')) {
mapply(function(slot_name) as.list(slot(x,slot_name)),
slotNames(class(x)),
SIMPLIFY=FALSE)
} else {
.Internal(as.vector(x, "list"))
}
}
You still need to enter the vector of all your custom classes c('nssItem','geckoNss')
. I was unable to find a function that returns these classes. The result is not as well formed as above ...
as.list(temp)
#$value
#$value[[1]]
#[1] NA
#
#
#$text
#$text[[1]]
#[1] NA
#
#
#$prefix
#$prefix[[1]]
#[1] NA
#
#
#$type
#$type[[1]]
#[1] NA
source to share