Remove Environment (and list) elements based on their classes

Asked

Viewed 186 times

4

Consider the vectors:

a<-1:10
b<-1:10
c<-'a'
d<-'a'
e<-list('b')
f<-list('b')
g<-1.1
h<-1.1

The function below removes only one class (which is integer, in this case):

rm(list=names(Filter(is.integer,mget(ls()))))

I have tried modifying this function (e.g. using some operators inside it to be able to enter two or more classes) but so far I have not been able to perform this action.

Two-pronged:

  • how to remove more than one class (for example, integer and list) and leave the others in Environment?

  • how this function would look if I threw all the objects in a list and, within it, perform the elimination of these objects based on the classes?

3 answers

5

Call an anonymous function inside rm() not to affect the objects shown in ls().

a<-1:10
b<-1:10
c<-'a'
d<-'a'
e<-list('b')
f<-list('b')
g<-1.1
h<-1.1

rm(list = (function(x, types){

  envType <- sapply(x, function(x) {
    typeof(get(x)) # retorna um vetor (nomeado com o nome de cada objeto em ls()) com o tipo de cada objeto em ls()
  })

  out <- names(which(sapply(envType, function(x){
    any(x %in% types)
  }) == TRUE)) # retorna um vetor com os nomes dos objetos que obedecem a condição em "types".

  return(out)

})(ls(), c("list", "integer")) # observe que o argumento "type" é flexível
)

What returns:

> ls()
[1] "c" "d" "g" "h"

5

One option is to use the function is() which is more generic than is.integer, for example. It requires, as the second argument, the class that will be checked.

Put the result of mget(ls()) proposed in the question within a list may be useful to avoid recreating the list at each iteration of the function.

ambiente <- mget(ls())

So we could have a function that returns the name of the objects in a list that are of a certain class.

filtrar_classe <- function(classe, lista) {
  names(Filter(function(x) is(x, classe), lista))
}

Once the function is defined, we can apply it to the desired classes

classes <- c("list",  "integer") 

res <- classes %>% 
  lapply(filtrar_classe, lista = ambiente) %>% 
  unlist()
res
# [1] "e" "f" "a" "b"

Now that we have a vector of characters as an answer, we can pass its result as an argument to the function rm().

rm(list = res)
ls()
# [1] "ambiente"       "c"              "classes"        "d"             
# [5] "filtrar_classe" "g"              "h"              "res" 

tidyverse

If we want to leave the solution more "Tidy", we can do as follows

filtrar_tidy <- function(classe, lista) {
  keep(lista, ~ is(.x, classe)) %>% 
    names()
}

classes %>% 
  map(filtrar_tidy, lista = ambiente) %>% 
  flatten_chr()

About the option in the list

The idea is the same, just use the names together with [ to select items from the list you want to delete.

nova_lista <- ambiente[ !names(ambiente) %in% res ]
nova_lista
$c
[1] "a"   
$d
[1] "a"    
$g
[1] 1.1    
$h
[1] 1.1

4


Based on this reply, it is also possible to do so (more ""):

library(purrr)

remover <- ls() %>% 
  map(get) %>% 
  map(class) %>% 
  map_lgl(~.x %in% c("list",  "integer")) %>% 
  keep(.x = ls(envir = .GlobalEnv))

rm(list = remover)

ls()
# [1] "c" "d" "g" "h"

The functions get and ls has an argument envir to know "where" to perform the search. It is possible to pass a list to this argument to make the version with lists.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.