Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collapse nested list into square brackets

Tags:

list

r

knitr

I am looking to write a function that converts a nested list with an arbitrary length and number of levels into a string that may serve as input for a tree from the LaTeX package forest.

Below is how far I got. I managed to enclose every childless node in the tree in square brackets, but how do I retrieve the names of the intermediate nodes and concatenate them into a single string?

The string in the forest environment shows what I would like to convert my example list into.

\documentclass[a4paper]{article}
\usepackage{forest}
\begin{document}
<<list>>=
library("tidyverse")
nestedlist <- list("A"=list("B"=45:50, "C"=LETTERS[21:26],
    "D"=list("E"=7:10, "F"=list("G","H"))))                   
squarebrackets <- function(x){
    if(class(x) == "list")
        map(x, squarebrackets)
    else
        paste0("[",x,"]") %>%
            paste0(., collapse="")
}

squarebrackets(nestedlist)
@
\begin{forest}
[A[B[45][46][47][48][49][50]][C[U][V][W][X][Y][Z]][D[E[7][8][9][10]][F[G][H]]]]
\end{forest}
\end{document}
like image 888
Crocodopolis Avatar asked Dec 17 '25 13:12

Crocodopolis


1 Answers

One approach is to take advantage of the hierarchy of the names automatically created by unlist(). This will also make F=c("G", "H") and F=list("G", "H") be treated in the same way.

The following example will not allow for numbers in the node names, and the node names must be unique. This could perhaps be improved upon by using a rapply() approach instead.

Define alternative squarebrackets

squarebracketsAlt <- function(inlist){

  #create the name hierarchy
  storeList <- unlist(inlist)

  #get unique names which represents levels in hierarchy
  uniqueNames <- unique(unlist(strsplit(gsub("[0-9]", "", names(storeList)), "\\.")))

  #keep the names to search for length of node brackets
  vecNames <- names(storeList)
  storeVec <- paste0("[", storeList, "]")
  names(storeVec) <- vecNames

  for(i in  uniqueNames){

    #determine the two positions of the node brackets
    whereBrack <- grep(paste0("\\.",i, "\\."),
                       paste0(".", gsub("[0-9]", "", names(storeVec)), "."))

    #add the start bracket and node name to vector
    storeVec <- append(storeVec, paste0("[", i), after=(whereBrack[1]-1))
    #add the end bracket to vector
    storeVec <- append(storeVec, paste0("]") , after=(whereBrack[length(whereBrack)]+1))


  }
  #collapse and output
  cat(paste(storeVec, collapse=""))

}

Try it on your nested list:

nestedlist <- list("A"=list("B"=45:50, "C"=LETTERS[21:26],
                            "D"=list("E"=7:10, "F"=list("G","H")))) 

squarebracketsAlt(nestedlist)

output:

[A[B[45][46][47][48][49][50]][C[U][V][W][X][Y][Z]][D[E[7][8][9][10]][F[G][H]]]]

enter image description here

Example larger hierarchy:

nestedlist1 <- list("Ad fe"=list("B"=45:50, "C"=list("U"=letters[1:10],LETTERS[22:26]),
                                "D"=list("E"=7:10, "F"=list("G"=list("ZZ foo"=list("AA bar"=c(1:10),2,3,4,5)),"H", "C"))))  

squarebracketsAlt(nestedlist1)

output:

[Ad fe[B[45][46][47][48][49][50]][C[U[a][b][c][d][e][f][g][h][i][j]][V][W][X][Y][Z]][D[E[7][8][9][10]][F[G[ZZ foo[AA bar[1][2][3][4][5][6][7][8][9][10]][2][3][4][5]]][H][C]]]]

enter image description here

Real life example:

nestedlist2 <- list("Main Area"=
                     list("Fishing vessel"=c("trawler", "line", "skipper"), "Oil tanker"=c("Large", "Small", "Medium size"=
                          list("Barents Sea", "Norwegian Sea", "Kara Sea", "Greenland"))))
squarebracketsAlt(nestedlist2)

output:

[Main Area[Fishing vessel[trawler][line][skipper]][Oil tanker[Large][Small][Medium size[Barents Sea][Norwegian Sea][Kara Sea][Greenland]]]]

enter image description here


Example with leading numbers:

squarebracketsAltNum <- function(inlist){

  #create the name hierarchy
  storeList <- unlist(inlist)

  #get unique names which represents levels in hierarchy
  uniqueNames <- unique(paste(gsub("[A-z].*", "", unlist(strsplit(names(storeList), "\\."))),
                              unlist(strsplit(gsub("[0-9]", "", names(storeList)), "\\.")), sep=""))

  #keep the names to search for length of node brackets
  vecNames <- names(storeList)
  storeVec <- paste0("[", storeList, "]")
  names(storeVec) <- vecNames
  k <- 1

  for(i in  uniqueNames){
    cat(i, "\n")
    #determine the two positions of the node brackets
    whereBrack <- grep(paste0("\\.",i),
                       paste0(".", names(storeVec)))

    #change position of number and character
    namePaster <- unique(paste(unlist(strsplit(gsub("[0-9]", "", names(storeList)), "\\.")),
                               gsub("[A-z].*", "", unlist(strsplit(names(storeList), "\\."))), sep=""))[k]


    #add the start bracket and node name to vector
    storeVec <- append(storeVec, paste0("[", namePaster), after=(whereBrack[1]-1))
    #add the end bracket to vector
    storeVec <- append(storeVec, paste0("]") , after=(whereBrack[length(whereBrack)]+1))
    k <- k+1

  }
  #collapse and output
  cat(paste(storeVec, collapse=""))

}

No space between number and word/sentence. Fiddle with regexp to fix:

nestedlist <- list("100A"=list("4B"=45:50, "3C"=LETTERS[21:26],
                               "D"=list("E"=7:10, "78F"=c("G","H"))))          
squarebracketsAlt(nestedlist)

Output:

[A100[B4[45][46][47][48][49][50]][C3[U][V][W][X][Y][Z]][D[E[7][8][9][10]][F78[G][H]]]]
like image 169
henrik_ibsen Avatar answered Dec 20 '25 05:12

henrik_ibsen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!