Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R - 'unlist' methods for list of custom classes

Tags:

r

I have some custom classes, for example:

setClass("foo", slots = c(mat = "matrix"))

I want to handle how a list of foo objects are 'unlisted'.

mat <- matrix(rnorm(16), 4)
foo <- new("foo", mat = mat)
unlist(list(foo))

I thought perhaps creating methods for c (which I thought was used but perhaps incorrectly) and unlist would solve the problem.

S3 version

#' @export
unlist <- function(x, ...) UseMethod("unlist", x)

#' @export
unlist.default <- base::unlist
#' @method unlist foo
#' @export
unlist.foo <- function(x, ...){
  print("called foo unlist")
}

S4 version

#' @export
setMethod("unlist",
          signature = "foo",
          function(x, recursive = TRUE, use.names = TRUE){
            print("call foo unlist")
          })

c function

#' @export
setMethod("c", 
          signature = "foo",
          function(x, ..., recursive = FALSE){
            print("called foo c")
})

But I only see the confirmation message when I use c directly:

c(foo)
[1] "called foo c"

The unlist just returns the same object with no print message

unlist(list(foo))
[[1]]
An object of class "foo"
Slot "mat":
           [,1]       [,2]       [,3]        [,4]
[1,]  0.6711541 -0.2783441 -0.4707375 -0.23060105
[2,]  0.7408401  0.4076826  2.2757187 -0.48547413
[3,]  1.8640581  0.3610619 -0.4632473 -0.06498348
[4,] -0.5595930  0.6679157 -0.8142456  0.27499963

If I call unlist(foo) then I get the print message but I need to apply it on a list of the foo objects. Any thoughts on how I can have unlist deal with custom classes within a list?

Ultimately I want the following to return TRUE:

all.equal(unlist(list(foo)), unlist(list(mat)))
like image 572
cdeterman Avatar asked Oct 18 '16 19:10

cdeterman


1 Answers

I am afraid this is not possible. unlist determines the type of its output based on types of individual elements of the list it gets. If all elements are atomic, like in case the only element is foo (a matrix), it does something to its argument -- coerces arguments to common atomic vector (there is only one) and forgets most of its attributes (e.g. dimensions). However, it does not treat S4 objects (like foo), that happen to be based on atomic vectors, as atomic vectors: these S4 objects will be hence left intact in the result, and the result will be of type list. So calling unlist on list(foo) returns list(foo). The behavior is implemented in C (do_unlist in bind.c) and seems to me to be in line with the documentation.

To mimick the desired behavior in a subset of possible usages of unlist, one could probably implement a new class for a list of foo objects, define list for foo, and then define a new unlist for this list-of-foo class to behave similarly to how the default C implementation of unlist behaves on a list of atomic vectors (I haven't tried).

like image 67
Tomas Kalibera Avatar answered Nov 12 '22 10:11

Tomas Kalibera