Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing multiple named list components in within()

I am trying to remove a named component from a list, using within and rm. This works for a single component, but not for two or more. I am completely befuddled.

For example - this works

aa = list(a = 1:3, b = 2:5, cc = 1:5)
within(aa, {rm(a)})

the output from within will have just the non-removed components.

However, this does not:

aa = list(a = 1:3, b = 2:5, cc = 1:5)
within(aa, {rm(a); rm(b)})

Neither does this:

within(aa, {rm(a, b)})

The output from within will have all the components, with the ones I am trying to remove, set to NULL. Why?

like image 987
martin Avatar asked Sep 26 '22 02:09

martin


1 Answers

First, note the following behavior:

> aa = list(a = 1:3, b = 2:5, cc = 1:5)
>
> aa[c('a', 'b')] <- NULL
>
> aa
# $cc
# [1] 1 2 3 4 5

> aa = list(a = 1:3, b = 2:5, cc = 1:5)
>
> aa[c('a', 'b')] <- list(NULL, NULL)
>
> aa
# $a
# NULL
#
# $b
# NULL
#
# $cc
# [1] 1 2 3 4 5

Now let's look at the code for within.list:

within.list <- function (data, expr, ...) 
{
    parent <- parent.frame()
    e <- evalq(environment(), data, parent)
    eval(substitute(expr), e)
    l <- as.list(e)
    l <- l[!sapply(l, is.null)]
    nD <- length(del <- setdiff(names(data), (nl <- names(l))))
    data[nl] <- l
    if (nD) 
        data[del] <- if (nD == 1) NULL else vector("list", nD)
    data
}

Look in particular at the second to last line of the function. If the number of deleted items in the list is greater than one, the function is essentially calling aa[c('a', 'b')] <- list(NULL, NULL), because vector("list", 2) creates a two item list where each item is NULL. We can create our own version of within where we remove the else statement from the second to last line of the function:

mywithin <- function (data, expr, ...) 
{
    parent <- parent.frame()
    e <- evalq(environment(), data, parent)
    eval(substitute(expr), e)
    l <- as.list(e)
    l <- l[!sapply(l, is.null)]
    nD <- length(del <- setdiff(names(data), (nl <- names(l))))
    data[nl] <- l
    if (nD) data[del] <- NULL
    data
}

Now let's test it:

> aa = list(a = 1:3, b = 2:5, cc = 1:5)
>
> mywithin(aa, rm(a, b))
# $cc
# [1] 1 2 3 4 5

Now it works as expected!

like image 132
Cameron Bieganek Avatar answered Sep 30 '22 08:09

Cameron Bieganek