Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an S4 equivalent to unlist()?

Tags:

r

s4

I have some experience working with S4 objects and their slots, so I know how to access specific slots and sub-slots. What I'd like to learn is how to "de-slotify" an object in the way that unlist takes apart an S3 list.
My immediate goal is to have an S4 counterpart to one of my toys which returns the number of elements of an object:

lssize<-function(items){
            if (any(sapply(sapply(items,get),typeof)=='closure')){
        warning('Closures in list, will ignore.')
        items<-items[(sapply(sapply(bar,get),typeof)=='closure')!=TRUE]
    }
    sizes<-sapply(sapply(sapply(sapply(items,get,simplify=F), unlist,simplify=F), as.vector,simplify=F), length)
    return(sizes)
    }

(no fair laughing at my code :-) ). I am hoping not to have to write some recursion routine which extracts slots one at a time to convert them.

Edit: I know object.size will return the bytecount; not what I'm after here.

like image 470
Carl Witthoft Avatar asked Feb 10 '13 22:02

Carl Witthoft


1 Answers

(This is revised to be closer to a previous, deleted answer, using slotName and slot rather than relying on attributes). We could write a function that tests whether an instance is an S4 object, and if so extracts all the slots as a list and recurses

f = function(x) {
    if (isS4(x)) {
        nms <- slotNames(x)
        names(nms) <- nms
        lapply(lapply(nms, slot, object=x), f)
    } else x
}

and then

A = setClass("A", representation(x="numeric"))
B = setClass("B", representation(a="A", b="numeric"))
f(B())

to arrive at a plain old list that we could use for whatever purposes we want.

$a
$a$x
numeric(0)

$a$class
[1] "A"
attr(,"package")
[1] ".GlobalEnv"


$b
numeric(0)

$class
[1] "B"
attr(,"package")
[1] ".GlobalEnv"

f might need to be enhanced, e.g., to handle NULL values or S4 classes made from S3 classes via setOldClass. The code to validObject would be my choice of places to look for a more comprehensive traversal.

A generalization might make a visitor, along the lines of

visitLeavesWith <-
    function(object, FUN, ...)
{
    f = function(x) {
        if (isS4(x)) {
            slots <- setNames(slotNames(x), slotNames(x))
            lapply(lapply(slots, slot, object=x), f)
        } else FUN(x, ...)
    }
    f(object)
}

e.g.,

visitLeavesWith(B(), length)
like image 122
Martin Morgan Avatar answered Nov 18 '22 12:11

Martin Morgan