【问题标题】:Remove NULL element and unlist the local level list from a nested list in R删除 NULL 元素并从 R 中的嵌套列表中取消列出本地级别列表
【发布时间】:2014-06-20 15:07:20
【问题描述】:

假设我有一个列表,假设它具有三个级别:

tmp =list(list(list(c(2,9,10), NULL), c(1,3,4,6)), 7) 

这会输出

[[1]]
[[1]][[1]] 
[[1]][[1]][[1]]
[1]  2  9 10

[[1]][[1]][[2]]
NULL

[[1]][[2]]
[1] 1 3 4 6

[[2]]
[1] 7

我想删除 NULL 元素和列表的本地级别。即嵌套列表 tmp 只有 2 级,它变成了

tmp =list(list(c(2,9,10), c(1,3,4,6)), 7). 

也就是说,所需的输出将是以下内容:

tmp
[[1]]
[[1]][[1]]
[1]  2  9 10

[[1]][[2]]
[1] 1 3 4 6

[[2]]
[1] 7

我曾尝试搜索 NULL 的索引位置,但没有运气。此外,我不确定如何检测和取消列出列表中包含 NULL 元素的列表。 谢谢!

【问题讨论】:

    标签: r


    【解决方案1】:

    通常,您会删除平面列表中的 NULL 元素

    ll <- list( 1, 2, NULL, 3 )
    ll <- ll[ ! sapply(ll, is.null) ]
    

    如果你事先不知道结构,这是一个将这个解决方案与递归函数结合起来的明显案例:

    removeNullRec <- function( x ){  
      x <- x[ !sapply( x, is.null ) ]
      if( is.list(x) ){
        x <- lapply( x, removeNullRec)
      }
      return(x)
    }
    
    removeNullRec(tmp)
    
    [[1]]
    [[1]][[1]]
    [[1]][[1]][[1]]
    [1]  2  9 10
    
    
    [[1]][[2]]
    [1] 1 3 4 6
    
    
    [[2]]
    [1] 7
    

    编辑

    尽可能简单地重新表述问题总是好的。我从您的 cmets 了解到的是,(与 NULL 元素的出现无关)您希望将每个仅包含一个子元素的元素替换为子元素本身。还有另一种情况需要考虑:两个兄弟叶子也可以是NULL。所以让我们从一个稍微复杂一点的例子开始:

    tree <- list(
      list(
        list(
          list(
            list( NULL, NULL ),
            list( NULL, NULL )
          ),
          7
        ),
        list(
          list(
            list( c(1,2), NULL ),
            c(3,4)
    ))))
    

    这个孤立的树扁平化问题当然也可以通过应用递归方法得到最好的解决:

    flatTreeRec <- function( x ){
      if( is.list(x) ){
        # recursion
        x <- lapply( x, flatTree )
        # remove empty branches
        x <- x[ sapply( x, length ) > 0 ]
        # flat branches with only child
        if( length(x) == 1 ){
          x <- x[[1]]
        }
      }
      return(x)
    }
    
    flatTreeRec( removeNullRec(tree) )
    

    当然你也可以直接将这两个函数结合起来,避免两次给你的堆栈带来压力:

    removeNullAndFlatTreeRec <- function( x ){  
      x <- x[ !sapply( x, is.null ) ]
      if( is.list(x) ){
        x <- lapply( x, removeNullRec)
        x <- x[ sapply( x, length ) > 0 ]
        if( length(x) == 1 ){
          x <- x[[1]]
        }
      }
      return(x)
    }
    
    removeNullAndFlatTreeRec( tree )
    

    【讨论】:

    • 非常感谢!我想知道包含 NULL 元素的列表是否可以从嵌套列表中不列出。对于我上面的示例,如果在列表中检测到 NULL 元素,则我取消列出该列表,以便嵌套列表只有 2 个列表。即,列表(列表(c(2,9,10),c(1,3,4,6)),7)。您的函数保留与原始列表相同数量的列表。
    • 谢谢!您的代码适用于删除 NULL 元素。但是,它不会删除/取消列出包含 NULL 元素的列表。
    • @user2498497 在哪些情况下您要取消列出NULL 元素的兄弟姐妹?只有当它是唯一的兄弟姐妹并且是平坦的(即不包含任何孩子)?如果列表元素是层次结构中唯一的元素并且是扁平的,那又如何呢?尽管没有NULL 兄弟姐妹,但您是否希望它们不被列出? list( list( list( 1,2,3 ), NULL ) ) 的正确结果应该是什么?
    • 我的列表结构是一个嵌套列表,每个列表的长度为2。(本质上是二叉树)。如果节点为 NULL,则它不会有任何子节点。然后这个 NULL 元素应该包含在一个平面列表中,它的兄弟姐妹。由于我的(二叉树)列表结构,列表元素不会是层次结构中的唯一元素并且是扁平的。也因为列表结构,list(list(list(1,2,3), NULL)) 不会发生。但是,list(list(1, c(2,3)), NULL) 可能会发生。在这种情况下,正确的结果将是 list(1, c(2,3))。谢谢!
    【解决方案2】:

    我正在使用这个功能:

    removeNULL <- function(x){
        x <- Filter(Negate(is.null), x)
        if( is.list(x) ){
          x <- lapply( x, function(y) Filter(length, removeNULL(y)))
        }
        return(x)
    }
    

    它不仅会删除NULL元素,还会删除仅包含NULL元素的列表元素,例如下面示例中的A2$A2$format$font

    > A2
    $A2
    $A2$value
    [1] 9.9
    
    $A2$format
    $A2$format$numberFormat
    [1] "2Decimal"
    
    $A2$format$font
    $A2$format$font$name
    NULL
    
    $A2$format$font$bold
    NULL
    
    $A2$format$font$color
    NULL
    
    
    
    $A2$comment
    NULL
    
    
    > removeNULL(A2)
    $A2
    $A2$value
    [1] 9.9
    
    $A2$format
    $A2$format$numberFormat
    [1] "2Decimal"
    

    【讨论】:

    • 很抱歉打扰您@Stéphane,但我正在尝试在这里做类似的事情:stackoverflow.com/q/58245155/10841085,想知道您是否可以看看?我有一个闪亮输入的标签列表,并且想修改每个输入的标签,同时保留输入列表的结构。我已经定义了一个递归函数来执行此操作,但它改变了输入列表的结构。
    【解决方案3】:

    2020 年 6 月更新:现在也可以使用 rrapply-package 中的 rrapply(基础 rrapply 的修订版)来完成。使用how = "prune",我们可以从列表中删除所有NULL 元素,同时保持原始列表结构。例如,使用与Beasterfield 的响应中相同的列表对象:

    library(rrapply)
    
    ## Example 1 
    tmp <- list(list(list(c(2,9,10), NULL), c(1,3,4,6)), 7) 
    
    ## keep only non-NULL leafs
    rrapply(tmp, condition = Negate(is.null), how = "prune")
    #> [[1]]
    #> [[1]][[1]]
    #> [[1]][[1]][[1]]
    #> [1]  2  9 10
    #> 
    #> 
    #> [[1]][[2]]
    #> [1] 1 3 4 6
    #> 
    #> 
    #> [[2]]
    #> [1] 7
    
    
    ## Example 2
    tree <- list(
        list(
            list(
                list(
                    list( NULL, NULL ),
                    list( NULL, NULL )
                ),
                7
            ),
            list(
                list(
                    list( c(1,2), NULL ),
                    c(3,4)
                ))))
    
    ## branches with only NULLs are completely pruned
    rrapply(tree, condition = Negate(is.null), how = "prune")
    #> [[1]]
    #> [[1]][[1]]
    #> [[1]][[1]][[1]]
    #> [1] 7
    #> 
    #> 
    #> [[1]][[2]]
    #> [[1]][[2]][[1]]
    #> [[1]][[2]][[1]][[1]]
    #> [[1]][[2]][[1]][[1]][[1]]
    #> [1] 1 2
    #> 
    #> 
    #> [[1]][[2]][[1]][[2]]
    #> [1] 3 4
    

    【讨论】:

      猜你喜欢
      • 2016-01-05
      • 2015-08-06
      • 2014-12-19
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多