Skip to content

Commit 6fee38c

Browse files
committed
added condition
1 parent 7c59daa commit 6fee38c

File tree

3 files changed

+86
-9
lines changed

3 files changed

+86
-9
lines changed

R/tables.R

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,62 @@ type_size = function(DT) {
1919
}
2020

2121
tables = function(mb=type_size, order.col="NAME", width=80L,
22-
env=parent.frame(), silent=FALSE, index=FALSE)
22+
env=parent.frame(), silent=FALSE, index=FALSE, recursive=FALSE)
2323
{
2424
# Prints name, size and colnames of all data.tables in the calling environment by default
2525
mb_name = as.character(substitute(mb))
2626
if (isTRUE(mb)) { mb=type_size; mb_name="type_size" }
2727
names = ls(envir=env, all.names=TRUE) # include "hidden" objects (starting with .)
2828
obj = mget(names, envir=env) # doesn't copy; mget is ok with ... unlike get, #5197
29-
w = which(vapply_1b(obj, is.data.table))
30-
if (!length(w)) {
31-
if (!silent) catf("No objects of class data.table exist in %s\n", if (identical(env, .GlobalEnv)) ".GlobalEnv" else format(env))
29+
found_items = list()
30+
if (recursive) {
31+
agenda <- lapply(seq_along(obj), function(i) list(obj = obj[[i]], name = names[i]))
32+
visited_env <- new.env(hash = TRUE)
33+
34+
while (length(agenda) > 0L) {
35+
current_item <- agenda[[1L]]
36+
agenda[[1L]] <- NULL
37+
x <- current_item$obj
38+
x_name <- current_item$name
39+
if (is.data.table(x)) {
40+
found_items[[length(found_items) + 1L]] <- list(name = x_name, obj = x)
41+
next
42+
}
43+
if (is.list(x) && !is.data.frame(x)) {
44+
# Cycle detection
45+
addr <- address(x)
46+
if (exists(addr, envir = visited_env, inherits = FALSE)) next
47+
assign(addr, TRUE, envir = visited_env)
48+
49+
item_names <- names(x)
50+
children_to_add <- vector("list", length(x))
51+
for (i in seq_along(x)) {
52+
child_name <- if (!is.null(item_names) && nzchar(item_names[i])) {
53+
paste0(x_name, "$", item_names[i])
54+
} else {
55+
paste0(x_name, "[[", i, "]]")
56+
}
57+
children_to_add[[i]] <- list(obj = x[[i]], name = child_name)
58+
}
59+
agenda <- c(rev(children_to_add), agenda)
60+
}
61+
}
62+
} else {
63+
w = which(vapply_1b(obj, is.data.table))
64+
if (length(w)) {
65+
found_items = lapply(w, function(i) list(name=names[i], obj=obj[[i]]))
66+
}
67+
}
68+
if (!length(found_items)) { # MODIFIED: Check `found_items` instead of `w`
69+
if (!silent) catf("No objects of class data.table exist in %s%s\n",
70+
if (identical(env, .GlobalEnv)) ".GlobalEnv" else format(env),
71+
if (recursive) " (recursively)" else "") # NEW: More informative message
3272
return(invisible(data.table(NULL)))
3373
}
34-
info = data.table(NAME=names[w], NROW=0L, NCOL=0L, MB=0.0, COLS=list(), KEY=list(), INDICES=list())
35-
for (i in seq_along(w)) { # avoid rbindlist(lapply(DT_names)) in case of a large number of tables
36-
DT = obj[[w[i]]]
74+
info = data.table(NAME=vapply(found_items, `[[`, "name", FUN.VALUE=character(1L)),
75+
NROW=0L, NCOL=0L, MB=0.0, COLS=list(), KEY=list(), INDICES=list())
76+
for (i in seq_along(found_items)) { # avoid rbindlist(lapply(DT_names)) in case of a large number of tables
77+
DT = found_items[[i]]$obj
3778
set(info, i, "NROW", nrow(DT))
3879
set(info, i, "NCOL", ncol(DT))
3980
if (is.function(mb)) set(info, i, "MB", as.integer(mb(DT)/1048576L)) # i.e. 1024**2

inst/tests/tests.Rraw

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21365,3 +21365,28 @@ test(2328.1, levels(droplevels(DT)$f), character())
2136521365
DT[, i := integer()]
2136621366
DT[, f2 := factor()]
2136721367
test(2328.2, droplevels(DT), data.table(f=factor(), i=integer(), f2=factor()))
21368+
21369+
#2606
21370+
test(2329.1, {
21371+
dt1 <- data.table(a = 1)
21372+
lst <- list(inner = dt1)
21373+
res <- tables(recursive=TRUE)
21374+
any(res$NAME == "lst$inner")
21375+
}, TRUE)
21376+
test(2329.2, {
21377+
lst <- list(data.table(b = 2))
21378+
res <- tables(recursive=TRUE)
21379+
any(grepl("^lst\\[\\[1\\]\\]$", res$NAME))
21380+
}, TRUE)
21381+
test(2329.3, {
21382+
nested <- list(l1 = list(l2 = data.table(c = 3)))
21383+
res <- tables(recursive=TRUE)
21384+
any(res$NAME == "nested$l1$l2")
21385+
}, TRUE)
21386+
test(2329.4, {
21387+
cycle <- list()
21388+
cycle[[1]] <- cycle
21389+
cycle[[2]] <- data.table(x = 1)
21390+
res <- tables(recursive=TRUE)
21391+
any(res$NAME == "cycle[[2]]") && !"cycle[[1]]" %in% res$NAME
21392+
}, TRUE)

man/tables.Rd

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
Convenience function for concisely summarizing some metadata of all \code{data.table}s in memory (or an optionally specified environment).
66
}
77
\usage{
8-
tables(mb=type_size, order.col="NAME", width=80,
9-
env=parent.frame(), silent=FALSE, index=FALSE)
8+
tables(mb=type_size, order.col="NAME", width=80L,
9+
env=parent.frame(), silent=FALSE, index=FALSE,
10+
recursive=FALSE)
1011
}
1112
\arguments{
1213
\item{mb}{ a function which accepts a \code{data.table} and returns its size in bytes. By default, \code{type_size} (same as \code{TRUE}) provides a fast lower bound by excluding the size of character strings in R's global cache (which may be shared) and excluding the size of list column items (which also may be shared). A column \code{"MB"} is included in the output unless \code{FALSE} or \code{NULL}. }
@@ -15,6 +16,9 @@ tables(mb=type_size, order.col="NAME", width=80,
1516
\item{env}{ An \code{environment}, typically the \code{.GlobalEnv} by default, see Details. }
1617
\item{silent}{ \code{logical}; should the output be printed? }
1718
\item{index}{ \code{logical}; if \code{TRUE}, the column \code{INDICES} is added to indicate the indices assorted with each object, see \code{\link{indices}}. }
19+
\item{recursive}{ \code{logical}; if \code{TRUE}, \code{tables} will perform a full,
20+
iterative search into list objects to find nested data.tables.
21+
Defaults to \code{FALSE} for backward compatibility. }
1822
}
1923
\details{
2024
Usually \code{tables()} is executed at the prompt, where \code{parent.frame()} returns \code{.GlobalEnv}. \code{tables()} may also be useful inside functions where \code{parent.frame()} is the local scope of the function; in such a scenario, simply set it to \code{.GlobalEnv} to get the same behaviour as at prompt.
@@ -32,5 +36,12 @@ DT = data.table(A=1:10, B=letters[1:10])
3236
DT2 = data.table(A=1:10000, ColB=10000:1)
3337
setkey(DT,B)
3438
tables()
39+
40+
# Finding data.tables nested in a list
41+
dt_list <- list(a = data.table(x=1:5), b = data.table(y=6:10))
42+
# By default, nested tables are not shown:
43+
tables()
44+
# Use recursive=TRUE to find them:
45+
tables(recursive = TRUE)
3546
}
3647
\keyword{ data }

0 commit comments

Comments
 (0)