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()
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()
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
How to Set Ggplot X-Label Equal to Variable Name During Lapply
How to Substitute Symbols in a Language Object
Error Using T.Test() in R - Not Enough 'Y' Observations
Sum Columns Row-Wise with Similar Names
Align Points and Error Bars in Ggplot When Using 'Jitterdodge'
Compare Two Columns Element-Wise
Selecting Unique Rows in Matrix Using R
Cannot Install Library(Xlsx) in R and Look for an Alternative
Filtering a Dataframe Showing Only Duplicates
Select N Rows Above and Below Match
Grouping Factor Levels in a Data.Table
How to Manage Parallel Processing with Animated Ggplot2-Plot
How to Scrape Website with Form Using Rvest