Related
Suppose that I have a nested list like the following
test <- list(
a = data.frame(x = 1),
b = "foo",
c = list(
d = 1:5,
e = data.frame(y = 1),
f = "a",
list(g = "hello")
)
)
test
#> $a
#> x
#> 1 1
#>
#> $b
#> [1] "foo"
#>
#> $c
#> $c$d
#> [1] 1 2 3 4 5
#>
#> $c$e
#> y
#> 1 1
#>
#> $c$f
#> [1] "a"
#>
#> $c[[4]]
#> $c[[4]]$g
#> [1] "hello"
I want to know the location of character elements in this nested list. In this
case, I want to return a named vector or a named list with TRUE if the element
is a character and FALSE otherwise.
I can do that with rapply, that unlists everything:
rapply(test, is.character)
#> a.x b c.d c.e.y c.f c.g
#> FALSE TRUE FALSE FALSE TRUE TRUE
However, I can’t do that to find all dataframes because rapply() also unlists
dataframes (note that the first element is a.x and not only a).
rapply(test, is.data.frame)
#> a.x b c.d c.e.y c.f c.g
#> FALSE FALSE FALSE FALSE FALSE FALSE
Therefore, is there a way to find which elements of a nested list are dataframes?
Note that the solution should work with any number of levels in the nested
list.
I’m looking for a solution in base R only.
1) rrapply
library(rrapply)
cls <- c("data.frame", "ANY")
rrapply(test, f = is.data.frame, classes = cls, how = "unlist")
## a b c.d c.e c.f c.g
## TRUE FALSE FALSE TRUE FALSE FALSE
2) recursion
findDF <- function(x) {
if (is.data.frame(x)) TRUE
else if (is.list(x)) lapply(x, findDF)
else FALSE
}
unlist(findDF(test))
## a b c.d c.e c.f c.g
## TRUE FALSE FALSE TRUE FALSE FALSE
I am trying to assemble a list out of a vector and a row of a data
frame. The list will be passed to do.call() as the arguments to a
function. If the vector is length 1, no problem.
tbl <- tibble::tibble(a = 1:4,
b = letters[1:4])
vec <- 1
works <- c(avec = vec, as.list(tbl[1,]))
testit <- function(avec, a, b){
length(avec) + length(a) + length(b)
}
do.call(testit, works)
#> [1] 3
But it also needs to work with longer vectors
vec <- 1:2
broken <- c(avec = vec, as.list(tbl[2,]))# breaks apart avec
do.call(testit, broken)
#> Error in (function (avec, a, b) : unused arguments (avec1 = 1, avec2 = 2)
toomany <- list(avec = vec, as.list(tbl[2,]))#too many layers
do.call(testit, toomany)
#> Error in (function (avec, a, b) : argument "b" is missing, with no default
#what I want:
whatIwant <- list(avec = 1:2, a = 2, b = "b")
do.call(testit, whatIwant)
#> [1] 4
It doesn’t matter if a data frame, and I want solution to work with both
tibbles and dataframes anyhow.
adf <- data.frame(a = 1:4,
b = letters[1:4], stringsAsFactors = FALSE)
list(avec = vec, as.list(adf[1,]))
#> $avec
#> [1] 1 2
#>
#> [[2]]
#> [[2]]$a
#> [1] 1
#>
#> [[2]]$b
#> [1] "a"
Other things I’ve tried.
purrr::flatten(toomany) # breaks up avec again
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
#>
#> $a
#> [1] 2
#>
#> $b
#> [1] "b"
c(avec = vec, as.list(adf[1,]), recursive = TRUE)
#> avec1 avec2 a b
#> "1" "2" "1" "a"
list(avec = vec, as.vector(adf[1,]))
#> $avec
#> [1] 1 2
#>
#> [[2]]
#> a b
#> 1 1 a
list(vec, unlist(adf[1,]))
#> [[1]]
#> [1] 1 2
#>
#> [[2]]
#> a b
#> "1" "a"
I didn’t think this would be so hard! Do I have to assemble the list in
text and parse it? I’m missing something. Created on 2019-03-01 by the
reprex package (v0.2.0).
I have a list of lists and I want the sub-lists to all have the same length
i.e. to pad them with NAs if needed so they all reach the length of the longest list.
Mock example
list1 <- list(1, 2, 3)
list2 <- list(1, 2, 3, 4, 5)
list3 <- list(1, 2, 3, 4, 5, 6)
list_lists <- list(list1, list2, list3)
My best attempt yet
max_length <- max(unlist(lapply (list_lists, FUN = length)))
# returns the length of the longest list
list_lists <- lapply (list_lists, function (x) length (x) <- max_length)
Problem, it is replacing all my sub-lists into an integer = max_length...
list_lists [[1]]
> [1] 6
Can someone help?
Try this (where ls is your list):
lapply(lapply(sapply(ls, unlist), "length<-", max(lengths(ls))), as.list)
In lists, NULL would seem more appropriate than NA, and could be added with vector:
list_lists <- list(list(1, 2, 3),
list(1, 2, 3, 4, 5),
list(1, 2, 3, 4, 5, 6))
list_lists2 <- Map(function(x, y){c(x, vector('list', length = y))},
list_lists,
max(lengths(list_lists)) - lengths(list_lists))
str(list_lists2)
#> List of 3
#> $ :List of 6
#> ..$ : num 1
#> ..$ : num 2
#> ..$ : num 3
#> ..$ : NULL
#> ..$ : NULL
#> ..$ : NULL
#> $ :List of 6
#> ..$ : num 1
#> ..$ : num 2
#> ..$ : num 3
#> ..$ : num 4
#> ..$ : num 5
#> ..$ : NULL
#> $ :List of 6
#> ..$ : num 1
#> ..$ : num 2
#> ..$ : num 3
#> ..$ : num 4
#> ..$ : num 5
#> ..$ : num 6
If you really want NAs, just change vector to rep:
list_lists3 <- Map(function(x, y){c(x, rep(NA, y))},
list_lists,
max(lengths(list_lists)) - lengths(list_lists))
str(list_lists3)
#> List of 3
#> $ :List of 6
#> ..$ : num 1
#> ..$ : num 2
#> ..$ : num 3
#> ..$ : logi NA
#> ..$ : logi NA
#> ..$ : logi NA
#> $ :List of 6
#> ..$ : num 1
#> ..$ : num 2
#> ..$ : num 3
#> ..$ : num 4
#> ..$ : num 5
#> ..$ : logi NA
#> $ :List of 6
#> ..$ : num 1
#> ..$ : num 2
#> ..$ : num 3
#> ..$ : num 4
#> ..$ : num 5
#> ..$ : num 6
Note the types in the latter won't match up unless you specify NA_real_ or coerce NA to match the type of x.
Here is your code fixed.
The function should return x, not length(x).
Also, I used vectors, not lists for clarity.
list1 <- c(1, 2, 3)
list2 <- c(1, 2, 3, 4, 5)
list3 <- c(1, 2, 3, 4, 5, 6)
list_lists <- list(list1, list2, list3)
max_length <- max(unlist(lapply (list_lists, FUN = length)))
list_lists <- lapply (list_lists, function (x) {length (x) <- max_length;x})
# [[1]]
# [1] 1 2 3 NA NA NA
#
# [[2]]
# [1] 1 2 3 4 5 NA
#
# [[3]]
# [1] 1 2 3 4 5 6
For original lists the result is:
# [[1]]
# [[1]][[1]]
# [1] 1
#
# [[1]][[2]]
# [1] 2
#
# [[1]][[3]]
# [1] 3
#
# [[1]][[4]]
# NULL
#
# [[1]][[5]]
# NULL
#
# [[1]][[6]]
# NULL
#
#
# [[2]]
# [[2]][[1]]
# [1] 1
#
# [[2]][[2]]
# [1] 2
#
# [[2]][[3]]
# [1] 3
#
# [[2]][[4]]
# [1] 4
#
# [[2]][[5]]
# [1] 5
#
# [[2]][[6]]
# NULL
#
#
# [[3]]
# [[3]][[1]]
# [1] 1
#
# [[3]][[2]]
# [1] 2
#
# [[3]][[3]]
# [1] 3
#
# [[3]][[4]]
# [1] 4
#
# [[3]][[5]]
# [1] 5
#
# [[3]][[6]]
# [1] 6
Try this:
funJoeOld <- function(ls) {
list_length <- sapply(ls, length)
max_length <- max(list_length)
lapply(seq_along(ls), function(x) {
if (list_length[x] < max_length) {
c(ls[[x]], lapply(1:(max_length - list_length[x]), function(y) NA))
} else {
ls[[x]]
}
})
}
funJoeOld(list_lists)[[1]]
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] NA
[[5]]
[1] NA
[[6]]
[1] NA
Edit
Just wanted to illuminate how using the right tools in R makes a huge difference. Although my solution gives correct results, it is very inefficient. By replacing sapply(ls, length) with lengths as well as lapply(1:z, function(y) NA) with as.list(rep(NA, z)), we obtain almost a 15x speed up. Observe:
funJoeNew <- function(ls) {
list_length <- lengths(ls)
max_length <- max(list_length)
lapply(seq_along(ls), function(x) {
if (list_length[x] < max_length) {
c(ls[[x]], as.list(rep(NA, max_length - list_length[x])))
} else {
ls[[x]]
}
})
}
funAlistaire <- function(ls) {
Map(function(x, y){c(x, rep(NA, y))},
ls,
max(lengths(ls)) - lengths(ls))
}
fun989 <- function(ls) {
lapply(lapply(sapply(ls, unlist), "length<-", max(lengths(ls))), as.list)
}
Compare equality
set.seed(123)
samp_list <- lapply(sample(1000, replace = TRUE), function(x) {lapply(1:x, identity)})
## have to unlist as the NAs in 989 are of the integer
## variety and the NAs in Joe/Alistaire are logical
identical(sapply(fun989(samp_list), unlist), sapply(funJoeNew(samp_list), unlist))
[1] TRUE
identical(funJoeNew(samp_list), funAlistaire(samp_list))
[1] TRUE
Benchmarks
microbenchmark(funJoeOld(samp_list), funJoeNew(samp_list), fun989(samp_list),
funAlistaire(samp_list), times = 30, unit = "relative")
Unit: relative
expr min lq mean median uq max neval cld
funJoeOld(samp_list) 21.825878 23.269846 17.434447 20.803035 18.851403 4.8056784 30 c
funJoeNew(samp_list) 1.827741 1.841071 2.253294 1.667047 1.780324 2.4659653 30 ab
fun989(samp_list) 3.108230 3.563780 3.170320 3.790048 3.888632 0.9890681 30 b
funAli(samp_list) 1.000000 1.000000 1.000000 1.000000 1.000000 1.0000000 30 a
There are two take aways here:
Having a good understanding of the apply family of functions makes for
concise and efficient code (as can be seen in #alistaire's and #989's solution).
Understanding the nuances of base R in general can have considerable consequences
Not sure if you are you looking for this and you may use lengths function for lists:
list_lists <- list(unlist(list1), unlist(list2), unlist(list3))
list_lists1 <- lapply(list_lists, `length<-`, max(lengths(list_lists)))
list_lists1
> list_lists1
[[1]]
[1] 1 2 3 NA NA NA
[[2]]
[1] 1 2 3 4 5 NA
[[3]]
[1] 1 2 3 4 5 6
OR for lists of the lists, you can go one step further:
list_lists2 <- lapply(list_lists1,as.list)
> list_lists2
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 2
[[1]][[3]]
[1] 3
[[1]][[4]]
[1] NA
[[1]][[5]]
[1] NA
[[1]][[6]]
[1] NA
[[2]]
[[2]][[1]]
[1] 1
[[2]][[2]]
[1] 2
[[2]][[3]]
[1] 3
[[2]][[4]]
[1] 4
[[2]][[5]]
[1] 5
[[2]][[6]]
[1] NA
[[3]]
[[3]][[1]]
[1] 1
[[3]][[2]]
[1] 2
[[3]][[3]]
[1] 3
[[3]][[4]]
[1] 4
[[3]][[5]]
[1] 5
[[3]][[6]]
[1] 6
>
I have a nested list containing NULL elements, and I'd like to replace those with something else. For example:
l <- list(
NULL,
1,
list(
2,
NULL,
list(
3,
NULL
)
)
)
I want to replace the NULL elements with NA. The natural way to do this is to recursively loop over the list using rapply. I tried:
rapply(l, function(x) NA, classes = "NULL", how = "replace")
rapply(l, function(x) if(is.null(x)) NA else x, how = "replace")
Unfortunately, neither of these methods work, since rapply apparently ignores NULL elements.
How can I manipulate the NULL elements in a nested list?
I'm going to go with "use a version of rapply doesn't doesn't have weird behaviour with NULL". This is the simplest implementation I can think of:
simple_rapply <- function(x, fn)
{
if(is.list(x))
{
lapply(x, simple_rapply, fn)
} else
{
fn(x)
}
}
(rawr::rapply2, as mentioned in the comments by #rawr is a more sophisticated attempt.)
Now I can do the replacement using
simple_rapply(l, function(x) if(is.null(x)) NA else x)
This is what William Dunlap suggested in 2010 when this question was asked on Rhelp:
replaceInList <- function (x, FUN, ...)
{
if (is.list(x)) {
for (i in seq_along(x)) {
x[i] <- list(replaceInList(x[[i]], FUN, ...))
}
x
}
else FUN(x, ...)
}
replaceInList(l, function(x)if(is.null(x))NA else x)
This is a hack, but as far as hacks go, I think I'm somewhat happy with it.
lna <- eval(parse(text = gsub("NULL", "NA", deparse(l))))
str(lna)
#> List of 3
#> $ : logi NA
#> $ : num 1
#> $ :List of 3
#> ..$ : num 2
#> ..$ : logi NA
#> ..$ :List of 2
#> .. ..$ : num 3
#> .. ..$ : logi NA
Update:
If for some reason you needed "NULL" as a character entry in the list (corner case, much?) you can still use the above hack since it replaces the contents of the string, not the quotes, thus it just requires another step
l2 <- list(
NULL,
1,
list(
2,
"NULL",
list(
3,
NULL
)
)
)
lna2 <- eval(parse(text = gsub("NULL", "NA", deparse(l2))))
lna2_2 <- eval(parse(text = gsub('\\"NA\\"', '\"NULL\"', deparse(lna2))))
str(lna2_2)
#> List of 3
#> $ : logi NA
#> $ : num 1
#> $ :List of 3
#> ..$ : num 2
#> ..$ : chr "NULL"
#> ..$ :List of 2
#> .. ..$ : num 3
#> .. ..$ : logi NA
I wrapped the replacement inside the sapply, which makes it more readable/understandable to me, albeit less general.
replace_null <- function(x) {
lapply(x, function(x) {
if (is.list(x)){
replace_null(x)
} else{
if(is.null(x)) NA else(x)
}
})
}
replace_null(l)
This can also be done with rrapply() in the rrapply-package. Below are a few different ways we could replace the NULL elements in a nested list by NA values:
library(rrapply)
l <- list(
NULL,
1,
list(
2,
NULL,
list(
3,
NULL
)
)
)
## replace NULL by NA using only f
rrapply(l, f = function(x) if(is.null(x)) NA else x, how = "replace")
#> [[1]]
#> [1] NA
#>
#> [[2]]
#> [1] 1
#>
#> [[3]]
#> [[3]][[1]]
#> [1] 2
#>
#> [[3]][[2]]
#> [1] NA
#>
#> [[3]][[3]]
#> [[3]][[3]][[1]]
#> [1] 3
#>
#> [[3]][[3]][[2]]
#> [1] NA
## replace NULL by NA using condition argument
rrapply(l, condition = is.null, f = function(x) NA, how = "replace")
#> [[1]]
#> [1] NA
#>
#> [[2]]
#> [1] 1
#>
#> [[3]]
#> [[3]][[1]]
#> [1] 2
#>
#> [[3]][[2]]
#> [1] NA
#>
#> [[3]][[3]]
#> [[3]][[3]][[1]]
#> [1] 3
#>
#> [[3]][[3]][[2]]
#> [1] NA
## replace NULL by NA using condition and deflt arguments
rrapply(l, condition = Negate(is.null), deflt = NA, how = "list")
#> [[1]]
#> [1] NA
#>
#> [[2]]
#> [1] 1
#>
#> [[3]]
#> [[3]][[1]]
#> [1] 2
#>
#> [[3]][[2]]
#> [1] NA
#>
#> [[3]][[3]]
#> [[3]][[3]][[1]]
#> [1] 3
#>
#> [[3]][[3]][[2]]
#> [1] NA
We can also prune the NULL elements from the list altogether by setting how = "prune":
## keep only non-NULL elements
rrapply(l, condition = Negate(is.null), how = "prune")
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [[2]][[1]]
#> [1] 2
#>
#> [[2]][[2]]
#> [[2]][[2]][[1]]
#> [1] 3
Is there a (built'in/easy) way to recursively display the names of a interlinked list as a tree? (with possibly an output similar to the tree shell command. )
For instance with list X, with two column A and B, A consiting in two subcolumn a1 and a2
nametree(x)
X
├── A
│ ├── a1
│ └── a2
└── B
names(X) would just display [1] "A" "B"
Here is a recursive solution:
nametree <- function(X, prefix = "")
if( is.list(X) )
for( i in seq_along(X) ) {
cat( prefix, names(X)[i], "\n", sep="" )
nametree(X[[i]], paste0(prefix, " "))
}
X <- list(X = list( A = list( a1=1:10, a2=1:10 ), B = 1:10 ))
nametree(X)
# X
# A
# a1
# a2
# B
Displaying the tree structure with branches rather than spaces is slightly trickier:
nametree <- function(X, prefix1 = "", prefix2 = "", prefix3 = "", prefix4 = "")
if( is.list(X) )
for( i in seq_along(X) ) {
cat( if(i<length(X)) prefix1 else prefix3, names(X)[i], "\n", sep="" )
prefix <- if( i<length(X) ) prefix2 else prefix4
nametree(
X[[i]],
paste0(prefix, "├──"),
paste0(prefix, "│ "),
paste0(prefix, "└──"),
paste0(prefix, " ")
)
}
nametree(X)
# X
# +--A
# ¦ +--a1
# ¦ +--a2
# +--B
# +--C
# +--a
# +--b
A simple example:
> mylist <- list(A=data.frame(A1=1:3,A2=4:6),B=7:9)
> out <- lapply(mylist,names)
$A
[1] "A1" "A2"
$B
NULL
This assumes you only have dataframes one level below the list...so it's not recursive per se, but it sounds like this is similar to your data structure.
DrMike and Henrik's suggestion to use str(mylist) will be recursive and is, in fact, able to control both how deep into the structure and the display of the output.
SimonO101's example of recursion:
> df <- data.frame( A = runif(3) , B = runif(3) )
> ll <- list( A = df , B = list( C = df , D = df ) , E = 1 )
> str(ll)
List of 3
$ A:'data.frame': 3 obs. of 2 variables:
..$ A: num [1:3] 0.948 0.356 0.467
..$ B: num [1:3] 0.2319 0.7574 0.0312
$ B:List of 2
..$ C:'data.frame': 3 obs. of 2 variables:
.. ..$ A: num [1:3] 0.948 0.356 0.467
.. ..$ B: num [1:3] 0.2319 0.7574 0.0312
..$ D:'data.frame': 3 obs. of 2 variables:
.. ..$ A: num [1:3] 0.948 0.356 0.467
.. ..$ B: num [1:3] 0.2319 0.7574 0.0312
$ E: num 1
Some examples of output:
> str(mylist)
List of 2
$ A:'data.frame': 3 obs. of 2 variables:
..$ A1: int [1:3] 1 2 3
..$ A2: int [1:3] 4 5 6
$ B: int [1:3] 7 8 9
> str(mylist, give.attr=FALSE, give.length=FALSE, give.head=FALSE, vec.len=0,
indent.str="|", comp.str="----")
List of 2
|----A:'data.frame': 3 obs. of 2 variables:
| ..$ A1:NULL ...
| ..$ A2:NULL ...
|----B:NULL ...
You can use the data.tree package. For example:
x <- list( A = list( a1 = list(data = 1:10), b1 = list(data = 1:100 )), B = list(data = c(1, 3, 5) ))
library(data.tree)
xtree <- FromListSimple(x, nodeName = "X")
xtree
This prints out:
levelName
1 X
2 ¦--A
3 ¦ ¦--a1
4 ¦ °--b1
5 °--B
Or you can convert the data into a printable format:
print(xtree, maxData = function(node) if (is.null(node$data)) 0 else max(node$data))
Which shows:
levelName maxData
1 X 0
2 ¦--A 0
3 ¦ ¦--a1 10
4 ¦ °--b1 100
5 °--B 5
Finally, to show the names of a node:
names(xtree$children)
This prints:
[1] "A" "B"
Here's what I came up with, see function definition at the bottom.
sample data:
# a short list
l1 <- list(a = factor("1"), b = c(u = 3, v = 4), d= list(x=5, y =6), e= 8, f = 9)
# a longer list
l2 <- replicate(100, l1, simplify = F)
default way of printing a short list:
print_list(l1)
#> $a
#> [1] 1
#> Levels: 1
#> $b
#> u v
#> 3 4
#> $d
#> $x
#> [1] 5
#> $y
#> [1] 6
#> $e
#> [1] 8
#> $f
#> [1] 9
restrict to first 3 items when named :
print_list(l1,n_named = 3)
#> $a
#> [1] 1
#> Levels: 1
#> $b
#> u v
#> 3 4
#> $d
#> $x
#> [1] 5
#> $y
#> [1] 6
#> # + 2 named items
pass parameters to print()
print_list(l1, quote = TRUE)
#> $a
#> [1] "1"
#> Levels: "1"
#> $b
#> u v
#> 3 4
#> $d
#> $x
#> [1] 5
#> $y
#> [1] 6
#> $e
#> [1] 8
#> $f
#> [1] 9
use str() rather than print() on non list items:
print_list(l1, fun = str)
#> $a
#> Factor w/ 1 level "1": 1
#> $b
#> Named num [1:2] 3 4
#> - attr(*, "names")= chr [1:2] "u" "v"
#> $d
#> $x
#> num 5
#> $y
#> num 6
#> $e
#> num 8
#> $f
#> num 9
use invisible rather than print to display only names:
print_list(l1, fun = invisible)
#> $a
#>
#> $b
#>
#> $d
#> $x
#>
#> $y
#>
#> $e
#>
#> $f
#>
print long list with restrictions:
print_list(l2,n_named = 3, n_unnamed = 2)
#> [[1]]
#> $a
#> [1] 1
#> Levels: 1
#> $b
#> u v
#> 3 4
#> $d
#> $x
#> [1] 5
#> $y
#> [1] 6
#> # + 2 named items
#> [[2]]
#> $a
#> [1] 1
#> Levels: 1
#> $b
#> u v
#> 3 4
#> $d
#> $x
#> [1] 5
#> $y
#> [1] 6
#> # + 2 named items
#> # + 98 items
function code
#' print list nicely
#'
#' #param l list to print
#' #param n_named max number of named items to display if list/sublist contains only named items
#' #param n_unnamed max number of items to display if list/sublist contains unnamed items
#' #param fun function to use to print non list items
#' #param ... additional arguments passed to fun
#'
#' #return unchanged input
#' #export
print_list <- function(l,
n_named = 20,
n_unnamed = 6,
fun = print,
...){
dots <- list(...)
fun0 <- function(l) do.call(fun, c(list(l),dots))
print_list0(l, nm = NULL, i = NULL, indent = -2,
n_named = n_named, n_unnamed = n_unnamed , fun = fun0)
}
print_list0 <- function(l, nm = NULL, i = NULL, indent=-2,
n_named = 20,
n_unnamed = 6,
fun){
if(!is.null(nm)){
if(nm!=""){
cat(strrep(" ", indent), "$", nm,"\n",sep="")
} else {
cat(strrep(" ", indent), "[[", i,"]]\n",sep="")
}
}
if(is.data.frame(l) || !is.list(l)){
output <- capture.output(fun(l))
output <- paste(strrep(" ", indent), output, collapse="\n")
cat(output,"\n")
} else {
nm = allNames(l)
named <- all(nm != "")
if(named && length(l) > n_named){
n_unshowed <- length(l) - n_named
l <- l[seq_len(n_named)]
nm <- nm[seq_len(n_named)]
Map(print_list0, l, nm, i = seq_along(l), indent=indent+2,
n_named = n_named, n_unnamed = n_unnamed,
fun = replicate(length(l), fun))
cat(strrep(" ", indent+2), "# + ", n_unshowed, " named items\n",sep="")
} else if(length(l) > n_unnamed){
n_unshowed <- length(l) - n_unnamed
l <- l[seq_len(n_unnamed)]
nm <- nm[seq_len(n_unnamed)]
Map(print_list0, l, nm, i = seq_along(l), indent=indent+2,
n_named = n_named, n_unnamed = n_unnamed,
fun = replicate(length(l), fun))
cat(strrep(" ", indent+2), "# + ", n_unshowed, " items\n",sep="")
} else {
Map(print_list0, l, nm, i = seq_along(l), indent=indent+2,
n_named = n_named, n_unnamed = n_unnamed,
fun = replicate(length(l), fun))
}
}
invisible(l)
}