从嵌套列表中提取具有特定名称的所有元素

问题描述 投票:0回答:5

我有一些存档的 Slack 数据,我正在尝试获取一些关键消息属性。我通过愚蠢地展平整个列表,获取 data.frame 或 tibble 以及嵌套在某些单元格中的列表来完成此操作。随着这个数据集变得越来越大,我想更智能地从这个列表中挑选元素,这样当这个缓存变大时,就不会永远用我想要的元素创建 data.frame 或 tibble。

我试图将下面名为“type”的所有内容拉入向量或平面列表中,我可以将其作为数据框变量拉入。为了方便起见,我命名了文件夹和消息级别。谁有模型代码可以帮忙吗?

library(tidyverse)
    
l <- list(folder_1 = list(
  `msg_1-1` = list(type = "message",
               subtype = "channel_join",
               ts = "1585771048.000200",
               user = "UFUNNF8MA",
               text = "<@UFUNNF8MA> has joined the channel"),
  `msg_1-2` = list(type = "message",
                   subtype = "channel_purpose",
                   ts = "1585771049.000300",
                   user = "UNFUNQ8MA",
                   text = "<@UNFUNQ8MA> set the channel purpose: Talk about xyz")),
  folder_2 = list(
    `msg_2-1` = list(type = "message",
                  subtype = "channel_join",
                  ts = "1585771120.000200",
                  user = "UQKUNF8MA",
                  text = "<@UQKUNF8MA> has joined the channel")) 
)

# gets a specific element
print(l[[1]][[1]][["type"]])

# tried to get all elements named "type", but am not at the right list level to do so
print(purrr::map(l, "type"))
r purrr
5个回答
5
投票

根据所需的输出,我可能会在这里使用简单的递归函数。

get_elements <- function(x, element) {
  if(is.list(x))
  {
    if(element %in% names(x)) x[[element]]
    else lapply(x, get_elements, element = element)
  }
}

这允许:

get_elements(l, "type")
#> $folder_1
#> $folder_1$`msg_1-1`
#> [1] "message"
#> 
#> $folder_1$`msg_1-2`
#> [1] "message"
#> 
#> 
#> $folder_2
#> $folder_2$`msg_2-1`
#> [1] "message"

或者如果你想获得所有“用户”:

get_elements(l, "user")
#> $folder_1
#> $folder_1$`msg_1-1`
#> [1] "UFUNNF8MA"
#> 
#> $folder_1$`msg_1-2`
#> [1] "UNFUNQ8MA"
#> 
#> 
#> $folder_2
#> $folder_2$`msg_2-1`
#> [1] "UQKUNF8MA"

如果您希望将结果展平为向量,您显然可以取消列出结果。

unlist(get_elements(l, "type"))
#> folder_1.msg_1-1 folder_1.msg_1-2 folder_2.msg_2-1 
#>        "message"        "message"        "message" 

2
投票

正如OP提到的,这可以解决问题:

#Code
unlist(l)[grepl('.type',names(unlist(l)),fixed=T)]

输出:

folder_1.msg_1-1.type folder_1.msg_1-2.type folder_2.msg_2-1.type 
            "message"             "message"             "message" 

另一个选择是(非常感谢并归功于@Abdessabour Mtk

#Code1
purrr::map(l, ~ purrr::map(.x, "type"))

2
投票

另一种选择是在

rrapply()
包中使用
rrapply

library(rrapply)

## return unlisted vector
rrapply(l, condition = function(x, .xname) .xname == "type", how = "unlist")
#> folder_1.msg_1-1.type folder_1.msg_1-2.type folder_2.msg_2-1.type 
#>             "message"             "message"             "message"

## return melted data.frame
rrapply(l, condition = function(x, .xname) .xname == "type", how = "melt")
#>         L1      L2   L3   value
#> 1 folder_1 msg_1-1 type message
#> 2 folder_1 msg_1-2 type message
#> 3 folder_2 msg_2-1 type message

2
投票

与 @Duck 和 @Abdessabour Mtk 昨天提供的相关,purrr 有一个函数

map_depth()
,如果你知道它的名称以及它在层次结构中的深度,它可以让你获得一个命名属性。在爬行这个大的嵌套列表时非常有用,并且是上面嵌套
map()
调用的更简单的解决方案。

purrr::map_depth(l, 2, "type")

0
投票

好吧,我想要一个基本的 R 解决方案,并且对 @Allan Cameron 的答案不满意,因为我想要将所有匹配项分组到同一“根”级别的最终列表中的东西。我不想使用

unlist
来这样做,因为我希望匹配的对象可能是复杂的表,并且不想失去结构。 我认为
append
可能会成功......并且在玩了一会儿之后,我认为我得到了一些似乎有用的东西(在我和OP的案例中的列表中):

我用了艾伦的名字:

get_elements <- function(x, element) {
    newlist=list()
    for(elt in names(x)){
        if(elt == element) newlist=append(newlist,x[elt])
        else if(is.list(x[[elt]])) newlist=append(newlist,get_elements(x[[elt]],element) )
    }
    return(newlist)
}

不如

lapply
优雅(按照我的口味),但我不确定我可以用任何 *apply 函数做我想做的事...尽管我仍然觉得可以做一些更简单、更好的事情(也许用
do.call
?)但找不到...

OP 列表结果:

> get_elements(l,"user")                                                                                                                                                                                                                   
$user
[1] "UFUNNF8MA"

$user
[1] "UNFUNQ8MA"

$user
[1] "UQKUNF8MA"

> get_elements(l,"type")
$type
[1] "message"

$type
[1] "message"

$type
[1] "message"
© www.soinside.com 2019 - 2024. All rights reserved.