C. Braun
C. Braun

Reputation: 5211

Test nested lists for identical structure

Say I have some complex, nested lists:

x <- list(list(a = array(1:24, c(2,3,4)), b = array(1:6, c(3,1,2))), list(c = 'string1'), list(d = c(T, F, F), e = c(F, F, T)))
y <- list(list(a = array(24:1, c(2,3,4)), b = array(2:7, c(3,1,2))), list(c = 'string2'), list(d = c(F, F, F), e = c(T, T, T)))
z <- list(list(a = array(24:1, c(3,2,4)), b = array(2:7, c(3,1,2))), list(c = 'string2'), list(d = c(F, F, F), e = c(T, T, T)))

Is there a simple way to test if the structures of two of these lists are identical?

I am looking for some function that will return TRUE if the lists have the same nesting, names, lengths, and element types. For example:

> all.equal.structure(x, y)
[1] TRUE   # Values are different, but that doesn't matter
> all.equal.structure(y, z)
[1] FALSE  # Dimensions of a are different

Upvotes: 1

Views: 189

Answers (1)

C. Braun
C. Braun

Reputation: 5211

The str function can be manipulated to compare just the structures. By default, it outputs to the terminal and returns NULL, but we can get its output value by using capture.output:

> x.str <- capture.output(str(x))
> y.str <- capture.output(str(y))
> x.str
[1] "List of 3"                                            
[2] " $ :List of 2"                                        
[3] "  ..$ a: int [1:2, 1:3, 1:4] 1 2 3 4 5 6 7 8 9 10 ..."
[4] "  ..$ b: int [1:3, 1, 1:2] 1 2 3 4 5 6"               
[5] " $ :List of 1"                                        
[6] "  ..$ c: chr \"string1\""                             
[7] " $ :List of 2"                                        
[8] "  ..$ d: logi [1:3] TRUE FALSE FALSE"                 
[9] "  ..$ e: logi [1:3] FALSE FALSE TRUE"      
> y.str
[1] "List of 3"                                                     
[2] " $ :List of 2"                                                 
[3] "  ..$ a: int [1:2, 1:3, 1:4] 24 23 22 21 20 19 18 17 16 15 ..."
[4] "  ..$ b: int [1:3, 1, 1:2] 2 3 4 5 6 7"                        
[5] " $ :List of 1"                                                 
[6] "  ..$ c: chr \"string2\""                                      
[7] " $ :List of 2"                                                 
[8] "  ..$ d: logi [1:3] FALSE FALSE FALSE"                         
[9] "  ..$ e: logi [1:3] TRUE TRUE TRUE"    

str also has a parameter vec.len that can be set to 0, so that no vector elements are displayed in the output:

> x.str <- capture.output(str(x, vec.len = 0))
> x.str
[1] "List of 3"                            
[2] " $ :List of 2"                        
[3] "  ..$ a: int [1:2, 1:3, 1:4] NULL ..."
[4] "  ..$ b: int [1:3, 1, 1:2] NULL ..."  
[5] " $ :List of 1"                        
[6] "  ..$ c: chr  ..."                    
[7] " $ :List of 2"                        
[8] "  ..$ d: logi [1:3] NULL ..."         
[9] "  ..$ e: logi [1:3] NULL ..."   

Putting it all together we can make this function:

all.equal.structure <- function(x, y) {
    identical(capture.output(str(x, vec.len = 0)),
              capture.output(str(y, vec.len = 0)))
}

> all.equal.structure(x, y)
[1] TRUE
> all.equal.structure(y, z)
[1] FALSE

Upvotes: 3

Related Questions