TobiSonne
TobiSonne

Reputation: 1107

Filter list of data.tables by function argument

I'm writing on a function that should filter each data.table in a list.

An Example:

library(purrr)
library(magrittr)
library(data.table)

My.List <- 
  as.data.table(iris) %>% 
  split(by = "Species")

map(My.List, ~.x[Sepal.Length < 5.5])

That is exactly what I want my result to be. But the function should be very user friendly. This is my desired function and it was even better if I could have multiple conditions separated by a ,, like in dplyr's filter:

myfunction(My.List, Sepal.Length < 5.5)
myfunction(My.List, Sepal.Length < 5.5, Petal.Width > 1)

Upvotes: 0

Views: 246

Answers (2)

Onyambu
Onyambu

Reputation: 79228

Technically, this is called Non Standard Evaluation nse. For the tidyverse, you could check here for the NSE implementation

Anyway to answer your question:

library(tidyverse)
myfunction <- function(lst, ...){
  nms <- enquos(...)
  map(lst, ~filter(.x, !!!nms))
}

myfunction(My.List, Sepal.Length < 5.5)
myfunction(My.List, Sepal.Length < 5.5, Petal.Width > 1)

If interested in ONLY BASE R functions, you could do:

myfunction <- function(lst, ...){
  nms <- substitute(list(...))
  lapply(lst, function(x)x[Reduce("&", eval(nms, x)),])
 }

Upvotes: 3

Allan Cameron
Allan Cameron

Reputation: 173858

Here's a base R equivalent that also does the trick. It uses match.call to extract the expressions (if there are any), then uses lapply to iterate through the list, evaluating each expression in the context of each data frame in the list using eval. This produces a list of logical vectors for each data frame, which are then combined into a single vector with an "&" via Reduce. This subsets each data frame.

myfunction <- function(.list, ...)
{
  mc <- as.list(match.call())[-1]
  if(length(mc) == 1) return(.list)
  lapply(.list, function(df)
  {
    conds <- lapply(mc[-1], function(condition) eval(condition, envir = df))
    df[Reduce("&", conds),]
  })
}

Upvotes: 3

Related Questions