R: Get Element by Name from a Nested List

R: get element by name from a nested list

The best solution so far is the following:

rmatch <- function(x, name) {
pos <- match(name, names(x))
if (!is.na(pos)) return(x[[pos]])
for (el in x) {
if (class(el) == "list") {
out <- Recall(el, name)
if (!is.null(out)) return(out)
}
}
}

rmatch(smth, "a1")
[1] 1
rmatch(smth, "b3")
[1] 6

Full credit goes to @akrun for finding it and mbedward for posting it here

pull all elements with specific name from a nested list

Related to those provided by @Duck & @Abdessabour Mtk yesterday, purrr has a function map_depth() that will let you get a named attribute if you know its name and how deep it is in the hierarchy. REALLY useful when crawling this big nested lists, and is a simpler solution to the nested map() calls above.

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

Nested lists multiple levels get elements by name

There isn't very much data in these files. I'm not sure what you're expecting. There are data frames called years and quarters in both lists. They both contain dates and values. Most of the content is NA. Each is a time-series data set starting in 1962. You don't need httr, either. fromJSON accepts a URL as a source without content or GET. m <- map(1:2, ~fromJSON(d[.x], flatten = T) where d is a vector with the two URLs. I think that's what @John Nielsen was stating. I used map, but lapply works fine, too.

You're using brackets, but you don't have to.

library(tidyverse) # for map and ggplot
library(jsonlite)

# get the JSON data and flatten it
mm <- map(1:2, ~fromJSON(d[.x], flatten = T))

# name each list, based on the URL
names(mm) <- c("Domestic", "International")
attributes(mm) # validate change

You can access the content with the environment pane, to get an idea of what you have.

You can use $ to access the content or [[]]. You can use numbers or names with [[]].

all.equal(mm[[1]][[1]], mm$Domestic$years)
# [1] TRUE

all.equal(mm[["Domestic"]][["years"]], mm$Domestic$years)
# [1] TRUE

To extract the data or to make a data frame-only object, you just need to assign it to an object name.

newDF <- mm$Domestic$years

You don't have to create a new object, though. If you wanted to plot the Domestic years data frame, you can use it as it is.

mm$Domestic$years %>% 
mutate(year = as.integer(year),
value = as.numeric(value)) %>%
ggplot(aes(year, value)) + geom_path() + theme_bw()

Sample Image

The Domestic quarters data

mm$Domestic$quarters %>% 
mutate(value = as.numeric(value)) %>%
ggplot(aes(seq_along(value), value)) + geom_path() +
scale_x_continuous(labels = mm$Domestic$quarters[seq(1, 220, 50), ]$date,
breaks = seq(1, 220, 50)) +
xlab("") + theme_bw()

Sample Image

You can do the same thing with the International data.

mm$International$years %>% 
mutate(year = as.integer(year),
value = as.numeric(value)) %>%
ggplot(aes(year, value)) + geom_path() + theme_bw()

R: Find object by name in deeply nested list

Here's a function that will return the first match if found

find_name <- function(haystack, needle) {
if (hasName(haystack, needle)) {
haystack[[needle]]
} else if (is.list(haystack)) {
for (obj in haystack) {
ret <- Recall(obj, needle)
if (!is.null(ret)) return(ret)
}
} else {
NULL
}
}

find_name(my_list, "XY01")

We avoid lapply so the loop can break early if found.

The list pruning is really a separate issue. Better to attack that with a different function. This should work

list_prune <- function(list, depth=1) {
if (!is.list(list)) return(list)
if (depth>1) {
lapply(list, list_prune, depth = depth-1)
} else {
Filter(function(x) !is.list(x), list)
}
}

Then you could do

list_prune(find_name(my_list, "XY01"), 1)

or with pipes

find_name(my_list, "XY01") %>% list_prune(1)

How can I access data in a nested R list?

Let's look at some ways to do what you want:

data[[1]]

This returns the first element of the list, which is itself a list. You can use the $ subsetting shorthand, but the name of the first element is nonstandard. R prefers names that start with letters and include only alphanumeric characters, periods and underscores. You can escape this behavior with backticks:

data$`1`

If you want to access one of the elements of list 1 in your list of lists, you need to further subset. To get to doy, which is the third element of 1. You can do that four ways.

data[[1]][[3]]
data$`1`[[3]]
data[[1]]$doy
data$`1`$doy

How to directly select the same column from all nested lists within a list?

Assuming you have something like the following:

myList <- list(`0` = c(`1` = 10, `2` = 20, `3` = 30, `4` = 72),
`1` = c(`1` = 15, `2` = 9, `3` = 7))
myList
# $`0`
# 1 2 3 4
# 10 20 30 72
#
# $`1`
# 1 2 3
# 15 9 7

Use sapply() or lapply() to get into your list and extract whatever columns you want. Some examples.

# As a list of one-column data.frames
lapply(myList, `[`, 1)
# $`0`
# 1
# 10
#
# $`1`
# 1
# 15

# As a list of vectors
lapply(myList, `[[`, 1)
# $`0`
# [1] 10
#
# $`1`
# [1] 15

# As a named vector
sapply(myList, `[[`, 1)
# 0 1
# 10 15

# As an unnamed vector
unname(sapply(myList, `[[`, 1))
# [1] 10 15

Other variants of the syntax that also get you there include:

## Same output as above, different syntax
lapply(myList, function(x) x[1])
lapply(myList, function(x) x[[1]])
sapply(myList, function(x) x[[1]])
unname(sapply(myList, function(x) x[[1]]))

A Nested List Example

If you do have nested lists (lists within lists), try the following variants.

# An example nested list
myNestedList <- list(A = list(`0` = c(`1` = 10, `2` = 20, `3` = 30, `4` = 72),
`1` = c(`1` = 15, `2` = 9, `3` = 7)),
B = list(`0` = c(A = 11, B = 12, C = 13),
`1` = c(X = 14, Y = 15, Z = 16)))

# Run the following and see what you come up with....
lapply(unlist(myNestedList, recursive = FALSE), `[`, 1)
lapply(unlist(myNestedList, recursive = FALSE), `[[`, 1)
sapply(unlist(myNestedList, recursive = FALSE), `[[`, 1)
rapply(myNestedList, f=`[[`, ...=1, how="unlist")

Note that for lapply() and sapply() you need to use unlist(..., recursive = FALSE) while for rapply() (recursive apply), you refer to the list directly.

Extract specific list elements from a nested list in R?

Using nested sapply -

sapply(x, function(y) sum(sapply(y, head, 1)))
#[1] 3 9 3

Or combine each list into a matrix and take sum of first column/row.

sapply(x, function(y) sum(do.call(rbind, y)[, 1]))
#sapply(x, function(y) sum(do.call(cbind, y)[1, ]))

Access named nested list elements with string name in R

you can do

 nm <- c("repository","milestone","issues")
response[[nm]]

also we can use the purrr::pluck function

  purrr::pluck(response, !!!nm)

Extract names of second [given] level of nested list in R

A possible solution, based on purrr::map_depth:

library(tidyverse)

map_depth(x, 1, names) %>% unlist(use.names = F)

#> [1] "one_1" "one_2" "one_3" "two_1" "two_2"

Extract a vector from nested lists in R

Here is an option where we bind the nested elements to a more easily approachable format with rrapply, then, we get the index of 'good' columns, extract the corresponding 'index' elements from that position index by looping over in map2 (based on the the TRUE values), transpose the list , keep only the elements having greater than 0 length, flatten the list and set the names (if needed)

library(purrr)
library(rrapply)
library(stringr)
library(dplyr)
out <- rrapply(listMaster, how = 'bind')
i1 <- grep('good', names(out))
map2(out[i1-1], out[i1], `[`) %>%
transpose %>%
map( ~ keep(.x, lengths(.x) > 0)) %>%
flatten %>%
setNames(str_c('index.', seq_along(.)))

-output

$index.1
[1] 1 2 3 4 5

$index.2
[1] 6 7 8 8 10

$index.3
[1] 1 2 3

$index.4
[1] 4 5 6 7 8 9 10

$index.5
[1] 2 4 6 8 10

$index.6
[1] 1 3 5 7 9


Related Topics



Leave a reply



Submit